[{"data":1,"prerenderedAt":524},["ShallowReactive",2],{"footer-primary":3,"footer-secondary":93,"footer-description":119,"tv-around-the-world":121,"tv-around-the-world-seasons":131,"tv-around-the-world-episodes":148,"sales-reps":272},{"items":4},[5,29,49,69],{"id":6,"title":7,"url":8,"page":8,"children":9},"522e608a-77b0-4333-820d-d4f44be2ade1","Solutions",null,[10,15,20,25],{"id":11,"title":12,"url":8,"page":13},"fcafe85a-a798-4710-9e7a-776fe413aae5","Headless CMS",{"permalink":14},"/solutions/headless-cms",{"id":16,"title":17,"url":8,"page":18},"79972923-93cf-4777-9e32-5c9b0315fc10","Backend-as-a-Service",{"permalink":19},"/solutions/backend-as-a-service",{"id":21,"title":22,"url":8,"page":23},"0fa8d0c1-7b64-4f6f-939d-d7fdb99fc407","Product Information",{"permalink":24},"/solutions/product-information-management",{"id":26,"title":27,"url":28,"page":8},"63946d54-6052-4780-8ff4-91f5a9931dcc","100+ Things to Build","https://directus.io/blog/100-tools-apps-and-platforms-you-can-build-with-directus",{"id":30,"title":31,"url":8,"page":8,"children":32},"8ab4f9b1-f3e2-44d6-919b-011d91fe072f","Resources",[33,37,41,45],{"id":34,"title":35,"url":36,"page":8},"f951fb84-8777-4b84-9e91-996fe9d25483","Documentation","https://docs.directus.io",{"id":38,"title":39,"url":40,"page":8},"366febc7-a538-4c08-a326-e6204957f1e3","Guides","https://docs.directus.io/guides/",{"id":42,"title":43,"url":44,"page":8},"aeb9128e-1c5f-417f-863c-2449416433cd","Community","https://directus.chat",{"id":46,"title":47,"url":48,"page":8},"da1c2ed8-0a77-49b0-a903-49c56cb07de5","Release Notes","https://github.com/directus/directus/releases",{"id":50,"title":51,"url":8,"page":8,"children":52},"d61fae8c-7502-494a-822f-19ecff3d0256","Support",[53,57,61,65],{"id":54,"title":55,"url":56,"page":8},"8c43c781-7ebd-475f-a931-747e293c0a88","Issue Tracker","https://github.com/directus/directus/issues",{"id":58,"title":59,"url":60,"page":8},"d77bb78e-cf7b-4e01-932a-514414ba49d3","Feature Requests","https://github.com/directus/directus/discussions?discussions_q=is:open+sort:top",{"id":62,"title":63,"url":64,"page":8},"4346be2b-2c53-476e-b53b-becacec626a6","Community Chat","https://discord.com/channels/725371605378924594/741317677397704757",{"id":66,"title":67,"url":68,"page":8},"26c115d2-49f7-4edc-935e-d37d427fb89d","Cloud Dashboard","https://directus.cloud",{"id":70,"title":71,"url":8,"page":8,"children":72},"49141403-4f20-44ac-8453-25ace1265812","Organization",[73,78,84,88],{"id":74,"title":75,"url":76,"page":77},"1f36ea92-8a5e-47c8-914c-9822a8b9538a","About","/about",{"permalink":76},{"id":79,"title":80,"url":81,"page":82},"b84bf525-5471-4b14-a93c-225f6c386005","Careers","#",{"permalink":83},"/careers",{"id":85,"title":86,"url":87,"page":8},"86aabc3a-433d-434b-9efa-ad1d34be0a34","Brand Assets","https://drive.google.com/drive/folders/1lBOTba4RaA5ikqOn8Ewo4RYzD0XcymG9?usp=sharing",{"id":89,"title":90,"url":8,"page":91},"8d2fa1e3-198e-4405-81e1-2ceb858bc237","Contact",{"permalink":92},"/contact",{"items":94},[95,101,107,113],{"id":96,"title":97,"url":8,"page":98,"children":100},"8a1b7bfa-429d-4ffc-a650-2a5fdcf356da","Cloud Policies",{"permalink":99},"/cloud-policies",[],{"id":102,"title":103,"url":81,"page":104,"children":106},"bea848ef-828f-4306-8017-6b00ec5d4a0c","License",{"permalink":105},"/bsl",[],{"id":108,"title":109,"url":81,"page":110,"children":112},"4e914f47-4bee-42b7-b445-3119ee4196ef","Terms",{"permalink":111},"/terms",[],{"id":114,"title":115,"url":81,"page":116,"children":118},"ea69eda6-d317-4981-8421-fcabb1826bfd","Privacy",{"permalink":117},"/privacy",[],{"description":120},"\u003Cp>A composable backend to build your Headless CMS, BaaS, and more.&nbsp;\u003C/p>",{"id":122,"title":123,"logo":124,"cover":125,"tile":126,"announcement_text":8,"description":127,"slug":128,"one_liner":129,"card_text":8,"status":130,"sort":8},"563b467f-a99a-49e3-8612-ca25d6829182","Around the World","833ec641-2653-483b-8e39-622e37d42d4d","f6039176-76a2-419e-86a8-f80695c635bd","430ec649-8c29-4657-9425-2981fbae18c6","Directus hosts several regional user groups around the world. They meet in-person to show the cool and interesting things build with and for Directus. These are the recordings.","around-the-world","Enjoy talk recordings from our regional in-person user groups around the globe.","published",[132,139],{"id":133,"number":134,"show":122,"year":135,"episodes":136},"63ed15fc-e3f3-48cb-9d72-a437171d1001",2,"2024",[137,138],"380ee198-9850-4e7f-a849-e1f328b0b6c0","08bdd8ab-2852-46ca-8914-6caa9c88aa3f",{"id":140,"number":141,"show":122,"year":142,"episodes":143},"dac1d26f-8071-4acc-a556-3fc30d03496a",1,"2023",[144,145,146,147],"fa893567-1d64-43e6-9cfd-a79380b49d8e","c1bf3bba-c29b-4d08-91b2-9c53b86d21cf","0edf83f5-87d4-4ea1-8030-0dbf472d228f","a25a20b8-6c18-49fb-b0a1-84a57025aebb",[149,168,185,209,233,253],{"id":144,"slug":150,"vimeo_id":151,"description":152,"tile":153,"length":154,"resources":8,"people":155,"episode_number":141,"published":159,"title":160,"video_transcript_html":161,"video_transcript_text":162,"content":8,"seo":8,"status":130,"episode_people":163,"recommendations":165,"season":166},"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",21,[156],{"name":157,"url":158},"Andreas Morgner","https://github.com/amo42","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/p>\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/p>\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/p>\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/p>\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/p>\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/p>\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/p>\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/p>\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/p>\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/p>\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/p>\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/p>\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/p>\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/p>\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/p>\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/p>\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/p>\u003Cp>Speaker 1: to Isabelle and overdraft fee?\u003C/p>\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/p>\u003Cp>Speaker 1: It's okay. There's a little threat there.\u003C/p>\u003Cp>Speaker 0: Yeah. Yeah.\u003C/p>\u003Cp>Speaker 1: So how do they interact with it or do they always interact with it through you?\u003C/p>\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/p>\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/p>\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/p>\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/p>\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/p>\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/p>\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/p>\u003Cp>Yes. Like\u003C/p>\u003Cp>Speaker 1: scraping a web page knowing that\u003C/p>\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/p>\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/p>\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/p>\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/p>\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/p>\u003Cp>It's We\u003C/p>\u003Cp>Speaker 1: will start.\u003C/p>\u003Cp>Speaker 0: No. No. No. It's still referring to a wrong URL. I see.\u003C/p>\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/p>\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/p>\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/p>\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/p>\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/p>\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/p>\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/p>","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.",[164],"d8cca303-6d0c-4675-a89f-ad09c5265774",[],{"id":140,"number":141,"show":122,"year":142,"episodes":167},[144,145,146,147],{"id":145,"slug":169,"vimeo_id":170,"description":171,"tile":172,"length":154,"resources":8,"people":173,"episode_number":134,"published":159,"title":177,"video_transcript_html":178,"video_transcript_text":179,"content":8,"seo":8,"status":130,"episode_people":180,"recommendations":182,"season":183},"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",[174],{"name":175,"url":176},"Mariana Costa","https://marianacosta.xyz/","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/p>\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/p>\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/p>\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/p>\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/p>\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/p>\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/p>\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/p>\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/p>\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/p>\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/p>\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/p>\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/p>\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/p>\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/p>\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/p>\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/p>\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/p>\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/p>\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/p>\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/p>\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/p>\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/p>\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/p>\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/p>\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/p>\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/p>\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/p>\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/p>\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/p>\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/p>\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/p>\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/p>\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/p>\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/p>\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/p>\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/p>\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/p>\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/p>","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.",[181],"36bad2c5-6a4d-4f20-99ed-674d6e86821e",[],{"id":140,"number":141,"show":122,"year":142,"episodes":184},[144,145,146,147],{"id":146,"slug":186,"vimeo_id":187,"description":188,"tile":189,"length":190,"resources":191,"people":195,"episode_number":199,"published":200,"title":201,"video_transcript_html":202,"video_transcript_text":203,"content":8,"seo":8,"status":130,"episode_people":204,"recommendations":206,"season":207},"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",19,[192],{"name":193,"url":194},"Pixie Labs","https://www.pixielabs.io/",[196],{"name":197,"url":198},"David Somers","https://www.linkedin.com/in/jalada",3,"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/p>\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/p>\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/p>\u003Cp>Starting to shrink. Great. Who has written a custom extension? Okay. Great.\u003C/p>\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/p>\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/p>\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/p>\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/p>\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/p>\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/p>\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/p>\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/p>\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/p>\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/p>\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/p>\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/p>\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/p>\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/p>\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/p>\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/p>\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/p>\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/p>\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/p>\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/p>\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/p>\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/p>\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/p>\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/p>\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/p>\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/p>\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/p>\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/p>\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/p>\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/p>\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/p>\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/p>\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/p>\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/p>\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/p>\u003Cp>Right? So they're all ready to go. Just drop it on. Cool. Alright.\u003C/p>\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/p>\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/p>\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/p>\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/p>\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/p>\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/p>\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/p>\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/p>\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/p>\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/p>\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/p>\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/p>\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/p>\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/p>\u003Cp>Thanks for listening, if it wasn't.\u003C/p>","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.",[205],"6608dfaa-4ae5-4923-a28e-59b5a35f8aee",[],{"id":140,"number":141,"show":122,"year":142,"episodes":208},[144,145,146,147],{"id":147,"slug":210,"vimeo_id":211,"description":212,"tile":213,"length":214,"resources":215,"people":219,"episode_number":223,"published":224,"title":225,"video_transcript_html":226,"video_transcript_text":227,"content":8,"seo":8,"status":130,"episode_people":228,"recommendations":230,"season":231},"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",31,[216],{"name":217,"url":218},"SUNZINET","https://www.sunzinet.com/directus-agentur",[220],{"name":221,"url":222},"Stephan Schmitz","https://www.linkedin.com/in/stephan-schmitz-31391a256/",4,"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/p>\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/p>\u003Cp>Speaker 0: On the podium.\u003C/p>\u003Cp>Speaker 1: And I can mutate that.\u003C/p>\u003Cp>Speaker 0: Hey. Okay. So, again, my name is Stefan. Hello. Thanks for having me.\u003C/p>\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/p>\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/p>\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/p>\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/p>\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/p>\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/p>\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/p>\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/p>\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/p>\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/p>\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/p>\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/p>\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/p>\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/p>\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/p>\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/p>\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/p>\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/p>\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/p>\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/p>\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/p>\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/p>\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/p>\u003Cp>But So it stays\u003C/p>\u003Cp>Speaker 1: on top of everything else. You'll probably need to just\u003C/p>\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/p>\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/p>\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/p>\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/p>\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/p>\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/p>\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/p>\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/p>\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/p>\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/p>\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/p>\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/p>\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/p>\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/p>\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/p>\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/p>\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/p>\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/p>\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/p>\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/p>\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/p>\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/p>\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/p>","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.",[229],"0538945c-5eac-44c9-b756-0a82d2738018",[],{"id":140,"number":141,"show":122,"year":142,"episodes":232},[144,145,146,147],{"id":137,"slug":234,"vimeo_id":235,"description":236,"tile":237,"length":238,"resources":8,"people":239,"episode_number":243,"published":244,"title":245,"video_transcript_html":246,"video_transcript_text":247,"content":8,"seo":8,"status":130,"episode_people":248,"recommendations":250,"season":251},"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",15,[240],{"name":241,"url":242},"Kevin Lewis","https://directus.io/team/kevin-lewis",5,"2024-02-07","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/p>\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/p>\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/p>\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/p>\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/p>\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/p>\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/p>\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/p>\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/p>\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/p>\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/p>\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/p>\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/p>\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/p>\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/p>\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/p>\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/p>\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/p>\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/p>\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/p>\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/p>\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/p>\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/p>\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/p>\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/p>\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/p>\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/p>\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/p>\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/p>\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/p>\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/p>\u003Cp>Was it overengineered? Yes. Would I do it again? No. Would I use a spreadsheet next time?\u003C/p>\u003Cp>Yes. But we're here. We did it. I get to talk about it. Next, the notification system.\u003C/p>\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/p>\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/p>\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/p>\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/p>\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/p>\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/p>\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/p>\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/p>\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/p>\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/p>\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/p>\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/p>","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.",[249],"3d15805d-29c6-441d-8b9b-0c111859c90f",[],{"id":133,"number":134,"show":122,"year":135,"episodes":252},[137,138],{"id":138,"slug":254,"vimeo_id":255,"description":256,"tile":257,"length":258,"resources":8,"people":259,"episode_number":263,"published":244,"title":264,"video_transcript_html":265,"video_transcript_text":266,"content":8,"seo":8,"status":130,"episode_people":267,"recommendations":269,"season":270},"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",30,[260],{"name":261,"url":262},"Nils Jeschke","https://www.linkedin.com/in/nils-jeschke/",6,"Building a Card Game Discord Bot With Directus","\u003Cp>Speaker 0: My name\u003C/p>\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/p>\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/p>\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/p>\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/p>\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/p>\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/p>\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/p>\u003Cp>Fairly easy?\u003C/p>\u003Cp>Speaker 0: Oh, I'm not sure you can let me for validation. Sure. That sounds correct.\u003C/p>\u003Cp>Speaker 1: It's easier than just, here, have a Google Sheet.\u003C/p>\u003Cp>Speaker 0: I mean, you said you are real low. Yeah. We we, leapfrog leapfrog that.\u003C/p>\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/p>\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/p>\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/p>\u003Cp>Here I actually see the user database. A lot of gibberish.\u003C/p>\u003Cp>Speaker 2: Could you switch the light theme?\u003C/p>\u003Cp>Speaker 1: We can switch light theme. Yeah.\u003C/p>\u003Cp>Speaker 2: It would be better for the projector.\u003C/p>\u003Cp>Speaker 1: Now this is an older instance. Now I have to see where it was in here.\u003C/p>\u003Cp>Speaker 0: Right there. Yeah.\u003C/p>\u003Cp>Speaker 1: Right there.\u003C/p>\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/p>\u003Cp>What was it? It's a 10 by 4. It's not write that theme.\u003C/p>\u003Cp>Speaker 1: There we go.\u003C/p>\u003Cp>Speaker 0: Yeah. That's better.\u003C/p>\u003Cp>Speaker 1: It's easier to be read.\u003C/p>\u003Cp>Speaker 2: And now the database needs some time to load again.\u003C/p>\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/p>\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/p>\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/p>\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/p>\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/p>\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/p>\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/p>\u003Cp>Speaker 0: So\u003C/p>\u003Cp>Speaker 2: log What are you voting for?\u003C/p>\u003Cp>Speaker 1: Especially not especially. Essentially, for the popularity of that specific application.\u003C/p>\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/p>\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/p>\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/p>\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/p>\u003Cp>Speaker 0: And so this currency effectively in the game, the star\u003C/p>\u003Cp>Speaker 1: that's I\u003C/p>\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/p>\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/p>\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/p>\u003Cp>Speaker 0: And there's a trading mechanism.\u003C/p>\u003Cp>Speaker 1: Yeah. That's a trading mechanism of for which I need a second user to actually initiate it.\u003C/p>\u003Cp>Speaker 0: Well, we we all know there's a trading Yeah.\u003C/p>\u003Cp>Speaker 1: Yeah. Any questions?\u003C/p>\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/p>\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/p>\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/p>\u003Cp>Speaker 1: stuff\u003C/p>\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/p>\u003Cp>Speaker 1: Actually, no. It's besides handling data. Directors is in this stage not having any additional part in it.\u003C/p>\u003Cp>Speaker 0: There's also subscribing, you know, the same monthly that's not directors, though.\u003C/p>\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/p>\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/p>\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/p>\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/p>\u003Cp>Speaker 0: And you can only store from Discord, arbitrary user ID that means something outside of\u003C/p>\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/p>\u003Cp>Speaker 0: That's an interesting workaround to I have a question.\u003C/p>\u003Cp>Speaker 1: Yeah.\u003C/p>\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/p>\u003Cp>Speaker 1: Yeah.\u003C/p>\u003Cp>Speaker 0: Where's the bot running? Is it running as part of, like, the direct project or is it running elsewhere?\u003C/p>\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/p>\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/p>\u003Cp>Speaker 1: within directors and users? But\u003C/p>\u003Cp>Speaker 0: if you're not doing it here, we need we need, you know, talk about that possibility. So just curious.\u003C/p>\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/p>\u003Cp>Speaker 0: Yeah.\u003C/p>\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/p>\u003Cp>Mhmm.\u003C/p>\u003Cp>Speaker 1: Oh, nice. Hi, camera. What is this? Ah, there we go. The cards.\u003C/p>\u003Cp>Speaker 0: So at the moment when you create cards, you do it. It is done here within the Data Studio?\u003C/p>\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/p>\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/p>\u003Cp>Speaker 0: And it doesn't stop being available afterwards. It just stops\u003C/p>\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/p>\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/p>\u003Cp>Speaker 0: But you said it was randomized. Oh, no. It isn't. What's randomized? Are the claims Yes.\u003C/p>\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/p>\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/p>\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/p>\u003Cp>Speaker 1: 3 milliseconds? The initial response has to be done within 3 milliseconds.\u003C/p>\u003Cp>Speaker 0: 3?\u003C/p>\u003Cp>Speaker 1: Yes. Okay. 3 milliseconds initial response means you get a ping and you instantly send one back.\u003C/p>\u003Cp>Speaker 0: It's breaking. Yeah.\u003C/p>\u003Cp>Speaker 1: That's how much time they give you. That it's an ephemeral ready response.\u003C/p>\u003Cp>Speaker 0: Yeah.\u003C/p>\u003Cp>Speaker 1: It says 3 milliseconds in the docs even though it isn't.\u003C/p>\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/p>\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/p>\u003Cp>Speaker 1: that is\u003C/p>\u003Cp>Speaker 0: the case, but I have to believe there's some kind of limits there.\u003C/p>\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/p>\u003Cp>Speaker 0: Image optimizations, whether there's anything\u003C/p>\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/p>\u003Cp>Does it break now? I think it's going to break.\u003C/p>\u003Cp>Speaker 0: Yeah. And what's what broke there? Do you know why?\u003C/p>\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/p>\u003Cp>But\u003C/p>\u003Cp>Speaker 0: You you mean the video that I have the recording of and will take through an editing workflow before I\u003C/p>\u003Cp>Speaker 1: Oh, yeah. Definitely. That's cool. That's cool. Nah.\u003C/p>\u003Cp>Doesn't want to load it. It's actually not even loading the project image anymore. It shouldn't be.\u003C/p>\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/p>\u003Cp>Speaker 1: Right? You have no\u003C/p>\u003Cp>Speaker 0: more planned content. Right?\u003C/p>\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/p>\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/p>\u003Cp>Ah, nice. That's the response from Discord, by the way.\u003C/p>\u003Cp>Speaker 0: Yeah. The message is there. Come on. Yeah.\u003C/p>\u003Cp>Speaker 1: And that's an interesting one. FaceTime. Interesting.\u003C/p>\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/p>\u003Cp>Speaker 3: database? I a database?\u003C/p>\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/p>","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.",[268],"34312cb6-c468-4edf-968b-b674542d1bbd",[],{"id":133,"number":134,"show":122,"year":135,"episodes":271},[137,138],{"reps":273},[274,330],{"name":275,"sdr":8,"link":276,"countries":277,"states":279},"John Daniels","https://meet.directus.io/meetings/john2144/john-contact-form-meeting",[278],"United States",[280,281,282,283,284,285,286,287,288,289,290,291,292,293,294,295,296,297,298,299,300,301,302,303,304,305,306,307,308,309,310,311,312,313,314,315,316,317,318,319,320,321,322,323,324,325,326,327,328,329],"Michigan","Indiana","Ohio","West Virginia","Kentucky","Virginia","Tennessee","North Carolina","South Carolina","Georgia","Florida","Alabama","Mississippi","New York","MI","IN","OH","WV","KY","VA","TN","NC","SC","GA","FL","AL","MS","NY","Connecticut","CT","Delaware","DE","Maine","ME","Maryland","MD","Massachusetts","MA","New Hampshire","NH","New Jersey","NJ","Pennsylvania","PA","Rhode Island","RI","Vermont","VT","Washington DC","DC",{"name":331,"link":332,"countries":333},"Michelle Riber","https://meetings.hubspot.com/mriber",[334,335,336,337,338,339,340,341,342,343,344,345,346,347,348,349,350,351,352,353,354,355,356,357,358,359,360,361,362,363,364,365,366,367,368,369,370,371,372,373,374,375,376,377,378,379,380,381,382,383,384,385,386,387,388,389,390,391,392,393,394,395,396,397,398,399,400,401,402,403,404,405,406,407,408,409,410,411,412,413,414,415,416,417,418,419,420,421,422,423,424,425,426,427,428,429,430,431,432,433,434,435,436,437,438,439,440,441,442,443,444,445,446,447,448,449,450,451,452,453,454,455,456,457,458,459,460,461,462,463,464,465,466,467,468,469,470,471,472,473,474,475,476,477,478,479,480,481,482,483,484,485,486,487,488,489,490,491,492,493,494,495,496,497,498,499,500,501,502,503,504,505,506,507,508,509,510,511,512,513,514,515,516,517,518,519,520,521,311,522,523],"Albania","ALB","Algeria","DZA","Andorra","AND","Angola","AGO","Austria","AUT","Belgium","BEL","Benin","BEN","Bosnia and Herzegovina","BIH","Botswana","BWA","Bulgaria","BGR","Burkina Faso","BFA","Burundi","BDI","Cameroon","CMR","Cape Verde","CPV","Central African Republic","CAF","Chad","TCD","Comoros","COM","Côte d'Ivoire","CIV","Croatia","HRV","Czech Republic","CZE","Democratic Republic of Congo","COD","Denmark","DNK","Djibouti","DJI","Egypt","EGY","Equatorial Guinea","GNQ","Eritrea","ERI","Estonia","EST","Eswatini","SWZ","Ethiopia","ETH","Finland","FIN","France","FRA","Gabon","GAB","Gambia","GMB","Ghana","GHA","Greece","GRC","Guinea","GIN","Guinea-Bissau","GNB","Hungary","HUN","Iceland","ISL","Ireland","IRL","Italy","ITA","Kenya","KEN","Latvia","LVA","Lesotho","LSO","Liberia","LBR","Libya","LBY","Liechtenstein","LIE","Lithuania","LTU","Luxembourg","LUX","Madagascar","MDG","Malawi","MWI","Mali","MLI","Malta","MLT","Mauritania","MRT","Mauritius","MUS","Moldova","MDA","Monaco","MCO","Montenegro","MNE","Morocco","MAR","Mozambique","MOZ","Namibia","NAM","Niger","NER","Nigeria","NGA","North Macedonia","MKD","Norway","NOR","Poland","POL","Portugal","PRT","Republic of Congo","COG","Romania","ROU","Rwanda","RWA","San Marino","SMR","São Tomé and Príncipe","STP","Senegal","SEN","Serbia","SRB","Seychelles","SYC","Sierra Leone","SLE","Slovakia","SVK","Slovenia","SVN","Somalia","SOM","South Africa","ZAF","South Sudan","SSD","Spain","ESP","Sudan","SDN","Sweden","SWE","Tanzania","TZA","Togo","TGO","Tunisia","TUN","Uganda","UGA","United Kingdom","GBR","Vatican City","VAT","Zambia","ZMB","Zimbabwe","ZWE","UK","Germany","Netherlands","Switzerland","CH","NL",1773850414364]