Adding Feature Flags to Next.js (Upstash Redis, SWR, Hooks)

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
hey how's it going everyone it's lee halliday and this video is all about feature flags and the flag i'd like to feature today is canada okay bad joke so what we're going to be doing is talking about feature flags which is in simple terms a way to turn functionality on and off on your website so imagine you're working on this multi-month project and instead of sort of blah here's everything all at once you can start pushing it to production in small little bits and hide it behind a feature flag so that you can control sort of when do you want to turn this on and unleash this amazing functionality to all of your users so we're going to be creating our own feature flag system in nextgs we're going to be learning how to do that with some custom hooks that we're creating api routes and we're going to be using up stash which is a serverless database for redis which is great because i've always found it difficult to find databases that work well with the pricing structure of serverless so here it's free to get up and running so feel free to check it out but at the end of the day it's just redis which gives you a nice key value store where we're going to be storing information about our feature flags okay so let's get going by heading over to our up stash console where we're going to create our first database and we're going to do this in the free tier which is more than enough for for a lot of people if you're just building a demo or work developing something locally and we'll give it a name of flipper we'll put this in the us east region and we are going to enable tls and strong consistency strong consistency just makes absolute sure it's written everything to the hard drive in the redis instance so this will just take a second to get up and running and what we're going to do is click redis connect and don't use this line right here for for our purposes um i've made some mistakes in the past it will not work and the reason it won't work is because we enabled tls and for tls to work with the io redis package that we're using you need this connection string so come and copy this sort of everything between these quotes and it should have the double s here which is standing for the secure redis connection string so then we can just close this and we're going to go into our code base so i'll give you a tour of the code base in a second but first things first we are going to go into a dot end file these you do not commit because it's containing your secrets so don't push this up to github and it's it's not being committed because it's in here in the uh get ignore file so we're going to call this redis url paste this connection string in and then we can just close this file so tour of the code base it's a next.js application and there's just a few packages installed so we've got ioretis for connecting to our redis database the typical next.js and react stuff and then swr which we're going to use to make an http request call in our react client i have also set up typescript it's a pretty sort of standard simple typescript setup the only really thing i've done is i've added in this alias to the src folder so that i don't have to do like dot slash dot dot dot slash and sort of try to figure out how many layers i have to go back you just sort of reference it with at source pretty cool you'll see that in a second and we're going to focus to start on this flags page so this is where we're going to be building an interface to manage the flags that are in our system so if we just start this app up yarndev go to the browser here okay so i actually want to go to flags which right now just says this and we need to get some flags into the system so we're going to first focus on how do we create the api endpoint to enable or create a new flag so to do that we've got inside of pages api flags we've got this square brackets key and then an enable file so the square brackets key is a placeholder for a dynamic sort of variable that's going to hit up this api endpoint so if we were to go to api flags login enable this is the key that's the dynamic part of the url so let's focus first on just getting it into a variable so const key will be a string and it's going to be equal to request dot query dot key like this and you see it's freaking out a little bit and the reason why is this here could be either a string or an array of strings so to make it happy we're just going to say this and then we actually don't need this it knows it's a key because we've told it or it's a string because we told it so if we want to make sure it's working we could just put the key here you can see it's picking it up as login so now what we want to do is basically pass this key to a function that's going to create and enable this flag inside of our redis database so we're going to import from src flags so remember this alias that i just mentioned and we're going to import a function called enable flag so this doesn't exist yet but we're going to call it a wait enable flag passing in the key and then we're going to go implement that function so src flags lives in src flags right here it's just got some a dummy function so what we're going to do first is create a connection to our redis database so we're going to say import redis from io redis i used io redis because it seems to work really well with promises and async await which is cool so we're going to say here const redis with a small r is equal to a new redis instance and we're going to pass in process process.nb.redis url which was that environment variable we set up and now we can export the function that we were calling so that would be export const what was it called enable flag is equal to an async function that takes a a key which is a string and then we want to do two calls to our redis database so we're going to await redis and the first way we're going to store our data is basically an array of all of the flags that we're tracking all of the keys now arrays can have duplicate values we don't want a duplicate here so instead we're going to use a set which is basically a unique array it gets rid of any duplicates so we're going to call a function called s add for set add and we're going to add it to the flags key and the value is just going to be this key so after that we are now going to call redis again and this time we're going to set a value so this keeps track of what are all the flags available this one's going to keep track of whether one flag is enabled or disabled so the key is going to be flag key like this and then the value or what we're going to use to represent enabled is just one um set stores string so we're just going to store a string of one or zero to say whether it's enabled or not so with this in place we can come back here refresh this it loads redis on up stash is extremely fast so it only takes like a couple milliseconds to to do these two requests um you can call these two requests at the exact same time using something called multi which exact executes these as an atomic operation um i will why don't i just add it in as a comment but we're not actually going to to use it um redis dot multi and then you say s add flags the key and then you do another command of set the flag with flag with the key value one and then you tell it to exec and this will basically execute both of these at once so i mean we could do this might as well be a little bit more efficient let's make sure i didn't screw it up still works so the last thing we wanted to do is basically get all of the flags back from a function that will be async called check all this doesn't return anything yet that's what we're going to return here so we need to go implement this function okay so the first thing we're going to do is define const check all and it's going to be an async function receives nothing but we first need to get a list of basically what are all of the flags or keys we have in our system so const keys is equal to a weight get keys so get keys doesn't exist so let's go implement it const get keys is equal to async and we are just going to return redis dot set members flags and this will return all of them back to us so if we were to just console.log the keys let's return a value so it's happy we do this um oh i know what the issue is we never imported it here there we go so here we have an array of all of our keys so now we want to get all of the values for all of those keys so yes you could call and get the value one by one but redis provides a way to sort of get all of the values all at once so what we are going to do is we'll say const values is equal to a weight redis dot multi get so this takes an array of keys to get all of the values for that so what we are going to do is we are going to map the keys so that each one looks like flag key like this so now we could console.log our values and see you can hear see here login is enabled so what i want to do now is basically convert this from these two arrays basically combine them into an object and we're going to do sort of an intermediary which is called a record and records are sorry not a record a map and a map is very similar to an object you can get and set keys and values in it and we're going to do that with a reduce so we're going to say const mapped is equal to so we're going to reduce the keys how do we do this again okay and then our callback function will be here so our initial value will be uh new map and since we're working in typescript the type is going to be a record where the key is a string and the value is going to be a boolean just like that so this thing's freaking out right now but we can get that fixed so we've got our accumulating map as we're iterating over each key and then we have the key so what we can do right here is we can say let's take our accumulator we'll set a value which will be the key sorry the key will be the key and then the value is going to be um what we also need is the index so we're going to find sort of the corresponding value which has the same index from this values array so what we'll do is we'll save values at index and we'll just check if it's equal to one like that it doesn't like this let's just keep going and then we're going to return the accumulator okay so i screwed up something big time let's try to figure out what oh or null um what we can just do is i must have messed this up before string is not assignable to key and value so the key string is not to string boolean man typescript okay figured out the issue sorry everyone okay so i screwed up figured out the issue i set the type wrong on a map you don't have to set it as a record x that um you just have to tell it what the key is and what the value is it's a boolean so everything's happy now now we end up with this mapped map which looks like this and we're going to basically return it as an object so how do you convert a map to an object you say object dot from entries the map just like this we can get rid of our console.logs this has flags and we output it so there we go there are our flags we probably can just pass return them like this there's our flag so key value of all the flags in the system and whether they're enabled or not that was a quite a bit of work just to enable a flag but now we get to go back to the front end and we're going to start showing them and we're going to create the ability to enable and disable them but before i even do that i want to go into the flags index and what i want to do is import check all from src flags and then what we're going to do is get the flags in a variable check all and we're going to return the flags so now anytime i want if i just want to get a list of all the flags i can just go to slash flags and it will return me this same response so with this in place we can go into the front end and we can start to show all of these flags on the screen so to do that i'm going to import use swr from swr this is going to help me do the http requests and what we're going to do is basically get these in into a variable so is equal to use swr and what we're going to do is pass slash api slash flags and you need a fetcher function for swr to work so a fetcher function we're going to create here it's going to receive this url so it's going to receive a url as a string and then it's going to fetch the url and then it's going to take the response and it's going to convert it to json just like that so as a response they give us data which we're going to rename to flags and we are going to also get access to an error cool so what we can do is we can say if there's an error let's just return sorry that if there's no data yet no flags we are going to return loading if we get to this point we know we have everything we need so what we can do is go inside of a ul and then we can say object dot entries of the flags and then map them so each one what we're going to be do given is an array where the first thing is basically the key of the flag and then the next one is the value whether it's enabled or not and we are going to output a list item so our key will be key and for now why don't we just put the value in here actually no let's put the key perfect so let's go see if this works loading here's our login flag so let's handle whether it's enabled or not so we're going to do that by before the key we're going to say so if value do a button so if it's enabled we want to disable else we want to enable like that so this one's enabled so we want to be able to disable it i've got these up here so i can say disable is this emoji enables this emoji and why don't we first handle how to disable a flag so we're going to say on click what we are going to do this is going to be an async function so the first thing we are going to do is basically we are going to call a new endpoint called disable so we are going to say await fetch calling api flags at key disable just like that so this doesn't exist so we need to go back and implement it so what we're going to do is we're going to import disable flag and check all from src flags then what we can do here is we need to get access to the key so why don't we just copy that line and then we can await disable flag of the key then we will get the flags by calling check all and we will send them as a response back perfect perfect except disable flag doesn't exist so let's go create that so we've got here export const disable flag which is async takes a key and what it's going to do is first things first it's going to remove the key from our set so we'll say redis dot s rim so from the flag set we will remove the key and then what we want to do is basically await redis um is it room or dell del no we're not deleting the flag we're just um so yeah what am i doing we're not removing this we're just disabling so what we're going to do is we're going to set the flag to zero all right that was easier than i expected so just set it to zero so we can come back when you click this refresh it's now enabled so why don't we go back here and then we can implement enabling so on click async function we're going to await a call to fetch api flags key enable like this and what i want to do is not have to refresh every time that i sort of change one of these values so what you can do here is you can say there's a mutate function and in this case if we just call mutate on its own it just basically reruns this query and updates our data so why don't we actually rename it to reload like this so then we can call reload each time we sort of change the value here so now we come back enable disable popping back and forth toggling on or off our feature flag so things are working pretty well why don't we put this in a span like that and then below why don't we create the ability to delete completely uh this flag so i've got my big x up here and on click we are going to basically do this again it's an async function except we're going to call remove instead so we need to go implement remove now so this is a process but we're making some headway so we're going to import [Music] remove flag and check all from src flags so we need to get the key which is going to be rec.query.flag as a string and then we will await remove flag passing in the key then we'll get the flags by calling check all and then pass the flags as the response so let's go to remove flag and implement this now okay so we'll put this below disable flag so export const remove flag is an async function that takes a key and the first thing this is what i i mixed up these two the first thing we're going to do is we're going to remove the flag from our set so we're going to say await redis dot set rem our flag set we're going to remove the key and then what i want to next do is we're going to say we're going to um delete the value from um flag colon key just like this okay with remove implemented we can come and try it out so if we go into our flags page click remove remove no nothing okay what the heck um [Music] so this seems fine here so if we go to the remove endpoint query ah i wasn't getting the right query parameter so now if we come back remove sorry i think i know why though it says sorry because it's it can't find any data i think so if we just change um here if not flags maybe if if flags is equal to undefined or flags is equal to null i don't know it was saying sorry that means there's an error that means wrong ah okay i know what we need to do then so we didn't handle a case what happens if there are no keys so i think what we should just do here is if um keys dot length is equal to 0 we just return an object like that we can't keep going so now it works but we need the ability to add a flag because now we don't have any which sucks so let's go back to our list here and we're going to go down below and do a form before we do that i want to just set up some some state some data so we are going to import use state from react and we're going to track the new flag that's supposed to be created use state empty string like this and then what we can do is come down here and create a form so our form what's it going to have it's going to have an input of type text it's going to be required and we're going to give it the value of the new flag and on change we are going to get the event and call set new flag with the event event.target.value okay and then we're going to have a button beside that that will just say add and this will be of type submit so if we were to come back here now we get this beautiful form but we have to handle the on submit oops so that will be equal to we'll get the event and we'll just say prevent default so now what we want to do is basically call the enable endpoint so we can get these two lines basically we have to make this into async where is the key it's actually the new flag state and we're going to want to reload after but we're also going to want to call set new flag and pass and make it back to an empty string so at enable it which will add it reload the data reset the form those are what we're trying to do so we'll add back login um it's enabled right now so i actually think i messed up my my things so if it's enabled we'll show what it is otherwise we will not cool so enabled disabled remove it add it back as enabled things are basically working here so now we get to let's actually do some functionality with this what i want to do is create a custom hook that will check whether or not the login is enabled so if we go to the home page it's got this login button but maybe i want to disable it for whatever reason our login is down let's disable it so if we go to the home page what i want to do is basically import a custom hook we're going to create called use flag so const um we'll we'll call uh we'll just check login enabled or we'll just call it login let's say and we're gonna call use flag and we're gonna check the login flag and then what we can do is we can basically check if login otherwise disabled so this obviously doesn't work yet because use flag doesn't exist so we're going to import it import use flag from src use flag and now we have to go implement it so we're going to hop down to use flag get rid of this and we're actually going to import use swr again from swr and we need another fetcher function so we'll just create this quickly so fetcher is a function that will take in a url and it will return fetch the url then convert the response to json perfect and we're going to export a function um when we export const use flag is going to be equal to taking in a key or a flag and the first thing we're going to do is basically you call use w use swr and that's going to give us back the data so it will be equal to use swr we're going to pass in our url so that would be api um ah we need to do some implementation too so api and then flags and then our key and then check our fetcher function and what we're going to basically just do is we're like force this data into a boolean so whatever it comes back if there's an error or not i don't care i'm just going to do the double conversion so convert it to false back to true or the opposite double opposite and um so this is a is a string like that and this will tell us whether or not it's enabled so we need to go implement quickly this check function so we can just come back here we need access again to our key so we'll paste this in we're going to import check one from src flags and then we're going to call it so const status is equal to a weight check 1 with the key and it's going to return the status of this specific feature flag key so check one doesn't exist let's go create it we'll just put it above check all i guess so export const check one which will take in a key as a string and then it's basically it just wants to read the value from redis so we'll get this value here and we'll await redis dot get the flag key and then we're just going to say it's true if it's equal to one everything else is false oops check one so that makes my typescript happy and now i can come back here and i can just say [Music] api flags login check so it's true right now so if we were to go to our flags page and disable it so now it's false so that means if we go to our home page you can't log in right now it's hidden but if we go and enable our flag now come back and it's you can log in so took us a long time to get here but basically right now we have a simple feature flag system that we can toggle on and off and we can use our custom hook to control whether bits and pieces of our website are enabled or not but maybe let's go one step further and this will be the last thing we do i've got this page called secret top secret what if i don't even want a user to be able to get here if if it's not enabled so first let's come here and let's create the feature flag called secret and let's just disable it right away so i want to redirect the user back to the home page if secret is not even enabled so in this case i don't want to do it client-side um i can just do it server-side since we're in next gs so let's go inside of this secret component right here and what we're going to do is create a get server side props function but it's going to be a type called a get server side props which is uh something we need to import from next and then that's going to be equal to an async function like this okay so the first thing we need to do is basically check to see if our secret flag is enabled or not so the cool thing is because we created that check 1 function from src flags we can actually just call it since we're on the server direct to go to redis and check to see if it's enabled so we can say the secret is equal to a weight check one secret like this so now what we can do is we can say if it's not enabled we are going to return a redirect back to the home page so we'll set the destination to the home page and we'll say that this is not permanent because we may enable it at a different point later this is still complaining and that's because we need to return what happens otherwise so at this point our server side function will run prior to rendering this secret page it will check to see if the secret feature flag is enabled and if it's not it will just kick the user back to the home page so if i refresh this kicks me back to the home page let's enable this let's go to secret again enabled now and now we can access this so this has been a pretty long video but we've covered a lot of things we've basically built our own feature flag system from start to finish so just to re recover what we've done we've created an endpoint to basically get all the all the flags we've created four other endpoints to check the status of one flag to disable a flag to enable or create a flag and then to to remove it to completely delete it from our redis database so inside of our flags file we sort of created all of these little mini functions who each sort of there's a counterpoint to these endpoints here to to do the actual work of talking to our up stash to our up stash redis database and we've got enable disable remove a little helper function to get all of the the flags all the keys that are available and then the ability to check one flag at a time or to check all of them at once you could try to take this a step further maybe check multi where you could pass in an array of flags and it would give you their status back it would be really easy to implement um do i want to do that now yes i do want to do it why not export const check multi so what does multi do it takes in an array of keys which is a string like that so what we can basically do is just copy all of this code and paste it in here so now what we can do is we can return check multi pass in the keys and there we go now you can call check all which will pass all the keys to check multi or you could just pass two of them or whatever so that would save you for example on your secret page say you wanted to also check the login now you could get back the status of secret and login oops but you wouldn't be check one it would be check multi and these would be an array anyways good times okay so then we sort of handled the front end of this we created the ability to manage our flags here to list them to disable enable basically working with the swr package and lastly what we did is we tried them out we took them for a test drive so we used our client-side use flag hook which will give us the answer back and then we can choose whether or not to render things but if you need it server side powered meaning the client shouldn't even visit a page for example you can do that like we did here inside of get server side props to load it directly from redis and just redirect the user or 404 or whatever you want to do as a response hope you enjoyed this video i've got a ton of other use cases for redis that i want to cover api throttling caching data so if you're interested let me know and we can cover those at a different point take care bye
Info
Channel: Leigh Halliday
Views: 3,136
Rating: 5 out of 5
Keywords: nextjs, feature flags, redis, upstash, custom hooks, react
Id: S1oOlKQo8CY
Channel Id: undefined
Length: 40min 59sec (2459 seconds)
Published: Wed May 19 2021
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.