[{"data":1,"prerenderedAt":439},["ShallowReactive",2],{"footer-primary":3,"footer-secondary":93,"footer-description":119,"around-the-world-extending-online-art-gallery":121,"around-the-world-extending-online-art-gallery-next":165,"sales-reps":187},{"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,"slug":123,"vimeo_id":124,"description":125,"tile":126,"length":127,"resources":128,"people":132,"episode_number":136,"published":137,"title":138,"video_transcript_html":139,"video_transcript_text":140,"content":8,"status":141,"episode_people":142,"recommendations":153,"season":154,"seo":8},"0edf83f5-87d4-4ea1-8030-0dbf472d228f","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,[129],{"name":130,"url":131},"Pixie Labs","https://www.pixielabs.io/",[133],{"name":134,"url":135},"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.","published",[143],{"people_id":144},{"id":145,"first_name":146,"last_name":147,"avatar":148,"bio":149,"links":150},"ef4c6f8e-591d-448c-ac96-805f5b7e53f4","David","Somers","de76f8e1-6bd7-4dbb-bd57-7e4952b1d514","Head of Engineering at Pixie Labs",[151],{"url":135,"service":152},"linkedin",[],{"id":155,"number":156,"year":157,"episodes":158,"show":162},"dac1d26f-8071-4acc-a556-3fc30d03496a",1,"2023",[159,160,122,161],"fa893567-1d64-43e6-9cfd-a79380b49d8e","c1bf3bba-c29b-4d08-91b2-9c53b86d21cf","a25a20b8-6c18-49fb-b0a1-84a57025aebb",{"title":163,"tile":164},"Around the World","430ec649-8c29-4657-9425-2981fbae18c6",{"id":161,"slug":166,"season":155,"vimeo_id":167,"description":168,"tile":169,"length":170,"resources":171,"people":175,"episode_number":179,"published":180,"title":181,"video_transcript_html":182,"video_transcript_text":183,"content":8,"seo":8,"status":141,"episode_people":184,"recommendations":186},"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,[172],{"name":173,"url":174},"SUNZINET","https://www.sunzinet.com/directus-agentur",[176],{"name":177,"url":178},"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.",[185],"0538945c-5eac-44c9-b756-0a82d2738018",[],{"reps":188},[189,245],{"name":190,"sdr":8,"link":191,"countries":192,"states":194},"John Daniels","https://meet.directus.io/meetings/john2144/john-contact-form-meeting",[193],"United States",[195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223,224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,240,241,242,243,244],"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":246,"link":247,"countries":248},"Michelle Riber","https://meetings.hubspot.com/mriber",[249,250,251,252,253,254,255,256,257,258,259,260,261,262,263,264,265,266,267,268,269,270,271,272,273,274,275,276,277,278,279,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,330,331,332,333,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,226,437,438],"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",1773850431953]