<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: József Sallai</title>
    <description>The latest articles on DEV Community by József Sallai (@jozsefsallai).</description>
    <link>https://dev.to/jozsefsallai</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F282483%2Fd4704501-a187-4f6f-b48a-8048daa5d6d5.png</url>
      <title>DEV Community: József Sallai</title>
      <link>https://dev.to/jozsefsallai</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/jozsefsallai"/>
    <language>en</language>
    <item>
      <title>The Most Disappointing Software Development Competition</title>
      <dc:creator>József Sallai</dc:creator>
      <pubDate>Wed, 06 Oct 2021 19:57:45 +0000</pubDate>
      <link>https://dev.to/jozsefsallai/the-most-disappointing-software-development-competition-31fj</link>
      <guid>https://dev.to/jozsefsallai/the-most-disappointing-software-development-competition-31fj</guid>
      <description>&lt;p&gt;&lt;em&gt;(Image translation: "Code Without Borders/Limits!")&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;This is a post that I've been wanting to write for a while to finally get it off my chest, mostly to bring awareness upon something I and two other friends of mine had to experience on a Hungarian software development competition.&lt;/p&gt;

&lt;h2&gt;
  
  
  First Things First - Who Am I?
&lt;/h2&gt;

&lt;p&gt;Some of you might be interacting with me through this blog post for the first time, so allow me to briefly introduce myself. My name is &lt;strong&gt;József Sallai&lt;/strong&gt; and I am a software engineer with many years of experience in the field of developing scalable, performant, and secure applications. I am also currently a university student at Sapientia Hungarian University of Transylvania where I'm majoring in Information and Computer Science.&lt;/p&gt;

&lt;p&gt;This article isn't only about me. It's also about two friends of mine and a government-endorsed company that benefited from our work for practically no cost at all.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Exactly Is This About?
&lt;/h2&gt;

&lt;p&gt;In winter of 2020, a news article was posted on our university's website about a &lt;a href="https://verseny.nisz.hu/verseny-2021/felsooktatas"&gt;competition&lt;/a&gt; which would allow us to test our abilities in software development and win some very cool prizes. What prizes, you might ask?&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--5Vata_9H--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/id597q3pqvvt7bwzxwfn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--5Vata_9H--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/id597q3pqvvt7bwzxwfn.png" alt="NISZ Competition Prizes"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Some very cool and expensive prizes, right? One would be a fool for not participating in a competition with prizes like that, especially since the organizers (&lt;a href="https://nisz.hu/en/"&gt;NISZ, National Infocommunications Services from Hungary&lt;/a&gt;), being endorsed by the Hungarian government, seem to be trustworthy.&lt;/p&gt;

&lt;p&gt;At least that's what we thought when &lt;a href="https://tohka.us"&gt;Daniel&lt;/a&gt;, &lt;a href="https://github.com/voidlord"&gt;Lehel&lt;/a&gt;, and I have signed up for the competition.&lt;/p&gt;

&lt;h2&gt;
  
  
  What We Had To Do
&lt;/h2&gt;

&lt;p&gt;On signup, we didn't know what we had to build, but judging by how much time we had to complete it and the value of the prizes, we were well aware that it's going to be a rather complex project. Which it turned out to be complex when we were notified about the nature of the app we were supposed to build.&lt;/p&gt;

&lt;p&gt;Long story short, we were assigned to build a &lt;strong&gt;web application platform&lt;/strong&gt; that would allow any two users of the app to create &lt;strong&gt;sale contracts&lt;/strong&gt; between each other, fill out the information required by the contract template, digitally sign it, and download it as a PDF. This is merely scratching the surface though since there's many other details and features that we had to keep in mind, however, for the sake of the blog post, this is enough information.&lt;/p&gt;

&lt;p&gt;When we signed up, we also had to agree to the terms of the competition, which also stated that &lt;strong&gt;the app we're building and its source code will be the property of NISZ&lt;/strong&gt;, so despite being advertised as an "open-source software development competition", everything we did was proprietary and belonged to NISZ (yes, even if we didn't win anything). Additionally, we were not allowed to deploy the app to our own servers, as that would have immediately disqualified us. Instead, we had to use the web server they've provided to us.&lt;/p&gt;

&lt;p&gt;The same document also had a paragraph about the prizes we would receive if we won the competition. Apart from the prizes mentioned earlier, the winning team would have also won a &lt;strong&gt;trip to the Hungarian Parliament, insights to how government-managed systems work, and a possibility for an internship at NISZ&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;These are all very important things to keep in mind as we progress further in the article.&lt;/p&gt;

&lt;h2&gt;
  
  
  What We've Built
&lt;/h2&gt;

&lt;p&gt;Our app is called &lt;strong&gt;Project Raccoon&lt;/strong&gt; (which was originally just going to be a codename but it stuck with us). It was a &lt;strong&gt;Next.js&lt;/strong&gt; app written in &lt;strong&gt;TypeScript&lt;/strong&gt; which used &lt;strong&gt;TypeORM&lt;/strong&gt; to communicate with a database of your choice. We've applied the best development practices that we were aware of in terms of designing and structuring the project's code. In total, we've written around &lt;strong&gt;30000 lines of code&lt;/strong&gt; for this project across multiple months.&lt;/p&gt;

&lt;p&gt;I also think it's worth mentioning that during the weeks we've worked on the app, we also had other duties, especially since we are university students. At least half of our day was dedicated to university activities and in the meantime, some of us have also done freelance work to earn some money. Besides, at the time this happened, I was going to driving school to get my license. Despite all of these things keeping us busy, we've managed to push our limits and create the best application we could, even if that meant having to skip classes or working on the app while also being in an online class meeting. I also had to postpone some of the work I was doing for money to make enough time for the competition.&lt;/p&gt;

&lt;p&gt;By now you should be able to tell that we've definitely put a lot of effort and dedication into this project. And we think it came out great. We don't often say this about the things we're making, but we were really proud of what we managed to pull off together as a team.&lt;/p&gt;

&lt;p&gt;At some point before the competition's deadline, we were asked about the tools we needed to deploy our app. Since this was a Next.js app, you could easily deploy it to a serverless platform and use cloud services in tandem, but to keep things simple, all we asked for was a &lt;strong&gt;generic web server that has Node.js and a relational database&lt;/strong&gt;. Nothing too complicated.&lt;/p&gt;

&lt;p&gt;Fast-forward a few weeks and what we got was some sort of &lt;strong&gt;shared VPS&lt;/strong&gt; that we had limited access to. Besides the limited access and the lack of a Node.js runtime in the beginning, the domain we were assigned to pointed to a &lt;strong&gt;PHP server&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Deadline
&lt;/h2&gt;

&lt;p&gt;The (initial) deadline for this project was &lt;strong&gt;April 23&lt;/strong&gt;. The app was ready for the base specification, albeit I do have to admit that, at the time, we were lacking some of the features required by the competition's description. Coincidentally, April 23 was also a day when we had an exam, so we had to be very careful with how we managed our time. In the morning, we got access to the web server and after lots of emailing back and forth asking them to install and reconfigure things on the server, we've managed to get the app up and running on their server. Shortly after we got done, the exam started, so there was a lot of stress and pressure going on that day.&lt;/p&gt;

&lt;p&gt;We found out that we were competing against &lt;strong&gt;3 other teams&lt;/strong&gt;, which, by the time of the deadline, were not ready with their projects. So their deployments were either non-existent or did not work at all. Which might have been one of the reasons the organizers of the competition decided to &lt;strong&gt;extend the deadline by a few days (April 27)&lt;/strong&gt;, to make sure the projects are getting completed and we have no issues with the deployment.&lt;/p&gt;

&lt;p&gt;We were delighted to hear this, since we noticed a few bugs in the meantime that we were able to fix thanks to the extended deadline.&lt;/p&gt;

&lt;p&gt;April 27 came, we have deployed the latest and greatest version of the app to the server provided by NISZ and we went on with our lives, patiently waiting for their feedback on it. In the meantime, some of the other teams were also able to get something deployed, even if it didn't have all the required functionality.&lt;/p&gt;

&lt;h2&gt;
  
  
  May
&lt;/h2&gt;

&lt;p&gt;After the deadline was extended, there was a strange radio silence on behalf of NISZ. No feedback, nothing has really changed in the apps or their content... We were starting to worry that something isn't right.&lt;/p&gt;

&lt;p&gt;Fast-forward to &lt;strong&gt;May 6&lt;/strong&gt;, when they finally replied, telling us that &lt;strong&gt;none of the applications had fulfilled all of the requirements of the competition&lt;/strong&gt;, and as such, they are giving every team &lt;strong&gt;one more week to implement the missing features&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;During our email exchange with NISZ, they've also &lt;strong&gt;congratulated us on our app&lt;/strong&gt;, saying that they've noticed how much effort went into it and how well it functions and looks. These comments made us more confident that our app will come out victorious and that we will win the competition. They've also told us that if we were to implement the missing features in this extra week, we are &lt;strong&gt;guaranteed to win the first prize&lt;/strong&gt;, however, they &lt;strong&gt;doubt&lt;/strong&gt; we will be able to deliver them in time.&lt;/p&gt;

&lt;p&gt;Briefly, the &lt;strong&gt;missing functionality&lt;/strong&gt; was:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A &lt;strong&gt;property system&lt;/strong&gt;, which would allow people to add their properties to a centralized database so they can easily generate a sale contract for them. *)&lt;/li&gt;
&lt;li&gt;A &lt;strong&gt;digital signature system&lt;/strong&gt; (we weren't sure if they meant PKCS 12 signatures for PDF or a frontend for signing documents using handwriting, so we did both).&lt;/li&gt;
&lt;li&gt;An &lt;strong&gt;attestation system&lt;/strong&gt;, which would ensure that the generated documents are authentic/legitimate.&lt;/li&gt;
&lt;li&gt;Adding &lt;strong&gt;attachments&lt;/strong&gt; to a contract.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;High-level authentication&lt;/strong&gt; (which we will talk about later). **)&lt;/li&gt;
&lt;li&gt;Adding &lt;strong&gt;witnesses and lawyers&lt;/strong&gt; to a contract.&lt;/li&gt;
&lt;li&gt;At least one &lt;strong&gt;bonus feature&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Some of the requirements in their email seemed vague to us, so we emailed them and called them via phone to ask for more details. The ones that were vague to us were:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;*) "Centralized inventory system", which we assumed meant each user would have their own "inventory" that they can use for generating contracts. We asked them about the way we've envisioned the implementation and all they said was "yes, that sounds good" (paraphrase).&lt;/li&gt;
&lt;li&gt;**) "Emelt szintű azonosítás", which basically stands for "higher-level identification". We assumed this meant the authorization flow for citizens which would hit an internal API to verify if the provided personal details were authentic. Once again, we asked them if our logic was what they were referring to, and they replied with "do it the way you think it would work the best".&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That may seem like a lot of work for one week, but that never stopped us. We worked hard until the last moment and delivered what we were asked to do...&lt;/p&gt;

&lt;h2&gt;
  
  
  ... or did we?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;May 12&lt;/strong&gt; came and we've submitted the &lt;strong&gt;final version of the application&lt;/strong&gt; to NISZ' web server, thinking that we did everything that was required by the competition rules. As an added bonus, we've added some &lt;strong&gt;unique features&lt;/strong&gt; to the app that weren't in the original specification:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A &lt;strong&gt;localization system&lt;/strong&gt;, which would allow foreigners to do business in Hungary.&lt;/li&gt;
&lt;li&gt;A &lt;strong&gt;light/dark mode switch&lt;/strong&gt; for those who don't want to burn their eyes.&lt;/li&gt;
&lt;li&gt;A &lt;strong&gt;specification and mock API&lt;/strong&gt; for the authorization/identification API that the app was supposed to check user data against.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Signature frontend&lt;/strong&gt; for those who want to add a handwritten signature.&lt;/li&gt;
&lt;li&gt;... and probably a few more features that I had forgotten about&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We were under the impression that not only did we complete the whole app as requested, but we've also overachieved in terms of user experience and even developer experience.&lt;/p&gt;

&lt;p&gt;After we've submitted the app, its documentation, and its source code, it was radio silence again... until &lt;strong&gt;May 17&lt;/strong&gt;, which is when they told us that&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Despite the additional week that we gave you, still &lt;strong&gt;none of the teams were able to fully finish the application as described in the requirements&lt;/strong&gt;. However, because we saw that your project was the most developed and required the most time and effort, we will give us a &lt;strong&gt;"special prize"&lt;/strong&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The way they worded it made us realize that the special prize they were referring to was a completely different mysteriously unmentioned prize and NOT any of the three prizes that were initially announced.&lt;/p&gt;

&lt;p&gt;In the same email message they've sent on the 17th, they've also told us that we had to &lt;strong&gt;create some sort of pitch video for the project&lt;/strong&gt;, which would describe the team, the app, and contain a brief demonstration of how the app works. The deadline was &lt;strong&gt;May 20&lt;/strong&gt;. Yep, we had to write the script, film everything, record the demonstration, and edit the video in &lt;strong&gt;less than 3 days&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;But despite the very tight deadline, we still managed to get it done. I wasted &lt;strong&gt;my entire birthday&lt;/strong&gt; (May 19) working on the video, recording and editing everything together, just so we can finish it off and send it to them by midnight.&lt;/p&gt;

&lt;p&gt;Once we've done that, they've thanked us for the video and told us that they will be back with more details soon.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Prize?
&lt;/h2&gt;

&lt;p&gt;A few weeks passed since we've submitted our pitch video. In further exchange, they have also told us that the missing functionality was&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;the centralized inventory system, the higher-level identification system, as well as better documentation&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Two out of those features could have been easily implemented if there was better communication between NISZ and us and they weren't so vague about the requirements. By "centralized inventory system" they were referring to a &lt;strong&gt;way for site administrators to see everything that gets added to the website&lt;/strong&gt;. The second one apparently refers to &lt;strong&gt;two-factor authentication&lt;/strong&gt;... I also need to mention that I've &lt;strong&gt;NEVER&lt;/strong&gt; seen any Hungarian application that would use the term "emelt szintű azonosítás" to refer to two-factor authentication. I've only ever seen "kétlépcsős hitelesítés" or "kétfaktoros hitelesítés".&lt;/p&gt;

&lt;p&gt;In fact, if we were to get pedantic, Googling "emelt szintű azonosítás" will only give you &lt;strong&gt;results to government websites and nisz.hu&lt;/strong&gt;. The very first result is from nisz.hu and it refers to &lt;strong&gt;a system that has the ability to ensure the authenticity of a user's personal information&lt;/strong&gt;. So I am very certain that the confusion here is very justified.&lt;/p&gt;

&lt;p&gt;Either way, in the grand scheme of things, that is pretty much &lt;strong&gt;2%&lt;/strong&gt; of the entire project, but for good measure I'll round it up to 5%.&lt;/p&gt;

&lt;p&gt;So 95% of the app was complete and fully functional.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;95%.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;That 5% was the reason we &lt;strong&gt;did not win anything&lt;/strong&gt; other than a special prize that was later revealed to be an &lt;strong&gt;e-learning kit&lt;/strong&gt; worth thousands of Hungarian Forints. For them though, this was nothing but a simple &lt;code&gt;UPDATE&lt;/code&gt; query in a database. I would also like to mention that the e-learning kit was actually the &lt;strong&gt;3rd prize for the high school&lt;/strong&gt; variant of the same competition.&lt;/p&gt;

&lt;p&gt;A project that was 95% done to the specification, ~30000 lines of code, many days of work, skipping classes and other duties, skipping celebrating a birthday only to finish a pitch video... and not only is the license of the project currently in a questionable situation, but we also &lt;strong&gt;did not receive anything valuable in return&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Oh, and did I mention that the other teams got &lt;strong&gt;nothing at all&lt;/strong&gt;? Even if their applications weren't fully done or didn't function well, I still feel like they deserved to get something for their work.&lt;/p&gt;

&lt;p&gt;And of course, the project and its code, as we agreed to when submitting it to their server, &lt;strong&gt;belongs to NISZ&lt;/strong&gt;. We cannot do anything with it (i.e. improve and monetize it) unless we get back our rights to the code. However, I also don't think it's fair that they can break the terms of their own contract at any point in time, so &lt;strong&gt;we still want to demand our rights to the code back&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Small Gift
&lt;/h2&gt;

&lt;p&gt;After a few weeks of being confused about the prize we got, on &lt;strong&gt;June 10&lt;/strong&gt;, NISZ emailed us saying&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;We want to give you a &lt;strong&gt;small&lt;/strong&gt; gift via post, to express our gratitude for joining the competition.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;They never specified what the physical gift they wanted to mail to us was, but since they specifically mentioned that it's a &lt;strong&gt;small&lt;/strong&gt; gift, we assumed it would be t-shirts or mugs, and perhaps a participation diploma.&lt;/p&gt;

&lt;p&gt;We've sent them our mailing addresses. They've &lt;strong&gt;confirmed that they've received the addresses&lt;/strong&gt; and that they will mail the gifts to us as soon as possible.&lt;/p&gt;

&lt;p&gt;Obviously, we've &lt;strong&gt;never received anything via post&lt;/strong&gt;, nor did they say anything ever since June 10. Color me surprised.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;We were, by all intents and purposes, massively screwed over by NISZ, which we assumed to be a trustworthy government-endorsed company. They are about to announce the competition again for 2021, but this time I want to &lt;strong&gt;urge everyone to avoid NISZ and this competition as much as possible&lt;/strong&gt;. If you really want to risk not receiving anything unless you do everything 100% as described in the spec (whatever that means, since they are always very vague about everything), then more power to you, but this should serve as a fair warning for what you're getting yourself into. 99.9% won't do, it has to be 100%.&lt;/p&gt;

&lt;p&gt;If anything, this was a learning experience to us. It doesn't matter if a company is endorsed by the government, it doesn't make it any more trustable than a random no-name company that announced a competition. Be careful with who you're collaborating with.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;This article was originally posted on &lt;a href="https://blog.sallai.me/nisz-competition-disappointment"&gt;my blog&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>watercooler</category>
    </item>
    <item>
      <title>Managing MongoDB Servers From The Browser Using WebMongo</title>
      <dc:creator>József Sallai</dc:creator>
      <pubDate>Sun, 03 May 2020 13:43:13 +0000</pubDate>
      <link>https://dev.to/jozsefsallai/managing-mongodb-servers-from-the-browser-using-webmongo-55jf</link>
      <guid>https://dev.to/jozsefsallai/managing-mongodb-servers-from-the-browser-using-webmongo-55jf</guid>
      <description>&lt;p&gt;&lt;strong&gt;MongoDB&lt;/strong&gt; is an awesome database system and I've always had so much fun working with it, especially paired with ODMs such as &lt;a href="https://www.npmjs.com/package/mongoose"&gt;Mongoose&lt;/a&gt;. Some of my friends that I've introduced to MongoDB, coming from a MySQL background, thought they had finally found Heaven, and while I don't necessarily agree with that hyperbole, I can see why they'd think it's so great. It's fast, easy to learn, very flexible and scalable, and just overall fun to work with.&lt;/p&gt;

&lt;p&gt;Another great thing about the MongoDB ecosystem is the existence of &lt;strong&gt;MongoDB Atlas&lt;/strong&gt;, an official Database-as-a-Service solution which is pretty affordable and easy to maintain. I think a good introduction to the serverless architecture is &lt;strong&gt;&lt;a href="https://www.mongodb.com/cloud/atlas"&gt;Atlas&lt;/a&gt; + &lt;a href="https://nextjs.org/"&gt;Next.js&lt;/a&gt; + &lt;a href="https://vercel.com/"&gt;Vercel&lt;/a&gt;&lt;/strong&gt; (I actually use this stack for some of my own apps too).&lt;/p&gt;

&lt;p&gt;However, every now and then, you might want to access your MongoDB server directly. Maybe to inspect some data or to change some stuff, but at some point you just can't fully rely on your app and let it do all the work.&lt;/p&gt;

&lt;h2&gt;
  
  
  MongoDB Clients
&lt;/h2&gt;

&lt;p&gt;If you can SSH into your server you can easily just run &lt;code&gt;mongo&lt;/code&gt; in the terminal and do everything you must do. But sometimes a GUI can be nice because you can better visualize the things you're working with. This is why GUI clients exist. These tools allow you to manage everything on your MongoDB server using a friendly graphical interface. &lt;strong&gt;The official desktop app - &lt;a href="https://www.mongodb.com/products/compass"&gt;Compass&lt;/a&gt;&lt;/strong&gt; - is very good for this task, for example.&lt;/p&gt;

&lt;p&gt;However, there's a small issue... and that is &lt;strong&gt;portability&lt;/strong&gt;. MongoDB Compass is only available for the desktop, so if you want to have a GUI client on your phone, you have to use third-party apps. Furthermore, if you're moving to a different workstation (for example, your school computer), you'd have to download and install Compass on it, which &lt;strong&gt;isn't very effective&lt;/strong&gt; and &lt;strong&gt;might not even work at all&lt;/strong&gt; (since schools and workplaces usually won't allow you to install new software).&lt;/p&gt;

&lt;p&gt;This can be an annoying task especially if you manage multiple servers, since you'd have to find the right connection string for each server and set it up accordingly.&lt;/p&gt;

&lt;h2&gt;
  
  
  What If...
&lt;/h2&gt;

&lt;p&gt;What if you could easily access, modify, and manage your MongoDB servers &lt;strong&gt;directly from your web browser&lt;/strong&gt;? It would be perfect, since there is pretty much no computer, tablet, or smartphone that doesn't have a web browser*. You could just enter a web address and you'd instantly have access to all your servers.&lt;/p&gt;

&lt;p&gt;But doesn't that imply that you'd have to store the admin credentials of your servers on someone's server? What if the web interface you're using has a security breach and someone will gain full access over everyone's connection strings?&lt;/p&gt;

&lt;p&gt;As soon as a website has a database with sensitive data and it also has a way to authenticate users, &lt;strong&gt;it can become a target for hackers&lt;/strong&gt;. So, is it really a good idea to use a web service for this after all?&lt;/p&gt;

&lt;p&gt;In this sense of things, it's probably not a good idea, especially if you're very security-conscious.&lt;/p&gt;

&lt;p&gt;*&lt;em&gt;) don't actually quote me on that&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  How I Tried To Solve This
&lt;/h2&gt;

&lt;p&gt;The web is a powerful invention that only gets more and more powerful. We can solve a lot of things more easily than we have imagined. This is why one night I was wondering - what if we &lt;strong&gt;didn't store anything at all&lt;/strong&gt;? If we don't store credentials, there's &lt;strong&gt;nothing that hackers could break into&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;This might sound pretty weird at first. You might be wondering how that would even work. And the answer to that is... well... just &lt;strong&gt;save it to the user's device&lt;/strong&gt;, duh! &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage"&gt;&lt;strong&gt;localStorage&lt;/strong&gt;&lt;/a&gt; is an amazing tool if you handle it correctly and this is what I took advantage of when creating &lt;strong&gt;&lt;a href="https://webmongo.now.sh"&gt;WebMongo&lt;/a&gt; - a fast and secure web-based GUI for managing MongoDB servers&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  About WebMongo
&lt;/h2&gt;

&lt;p&gt;This is a small passion project of mine with the goal of making a solid web interface for MongoDB without giving up on things like &lt;strong&gt;security&lt;/strong&gt;. It started as an experiment, mainly for me to see if something like this was indeed possible. And sure enough, it is! Even though it's still in beta and could definitely use some more improvements, the web app is in a fairly stable condition right now.&lt;/p&gt;

&lt;p&gt;WebMongo takes a different approach on storing and syncing data. Instead of saving your data to a centralized database, &lt;strong&gt;it saves it to your browser's local storage&lt;/strong&gt;. This is convenient because you don't have to worry about someone stealing your credentials, unless someone steals one of your physical devices (in which case your other credentials would probably be at risk too). For &lt;strong&gt;syncing&lt;/strong&gt;, there's an &lt;strong&gt;Import/Export feature&lt;/strong&gt;. The exported file is &lt;strong&gt;AES-encrypted with a passphrase that you provide&lt;/strong&gt;, which means you can save it in a cloud provider of your choice. That way &lt;strong&gt;you can access your servers anywhere, anytime&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Here's a list of what WebMongo is capable of doing at this moment:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Add and remove &lt;strong&gt;MongoDB servers&lt;/strong&gt; (or connection strings, rather)&lt;/li&gt;
&lt;li&gt;View &lt;strong&gt;databases&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;View, add, modify, and drop &lt;strong&gt;collections&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;View, add, modify, and delete &lt;strong&gt;documents&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Export&lt;/strong&gt; the list of your MongoDB servers as a &lt;strong&gt;passphrase-protected encrypted file&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lock your session using a passphrase&lt;/strong&gt; (encrypts the data that's saved in your browser)&lt;/li&gt;
&lt;li&gt;One-click "&lt;strong&gt;Delete All Data&lt;/strong&gt;" button to completely get rid of all data (useful if you needed it temporarily, for example, on a shared computer)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;PWA&lt;/strong&gt; for easy installation and quick access&lt;/li&gt;
&lt;li&gt;Due to its &lt;strong&gt;open-source&lt;/strong&gt; nature, you can &lt;strong&gt;deploy your own instance&lt;/strong&gt; of WebMongo, making it &lt;strong&gt;100% decentralized&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Tech
&lt;/h2&gt;

&lt;p&gt;I won't go in-detail into describing how I made WebMongo, but give you an overview of the stack that I used for it.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Frontend: &lt;a href="https://sapper.svelte.dev/"&gt;Sapper (Svelte)&lt;/a&gt;&lt;/strong&gt;. A tiny web framework that has always helped me develop stuff quickly.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Backend: &lt;a href="https://expressjs.com/"&gt;Express.js&lt;/a&gt;&lt;/strong&gt;. Express is only used to communicate with your MongoDB server and process your queries. Nothing else. It uses the value of the &lt;code&gt;Connection-String&lt;/code&gt; header to figure out how to connect to a server.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Deployment: &lt;a href="https://vercel.com"&gt;Vercel&lt;/a&gt;&lt;/strong&gt;. I love serverless architecture and Vercel makes it accessible for everyone. With their &lt;a href="https://vercel.com/blog/simpler-pricing"&gt;nearly unlimited plans&lt;/a&gt; and super easy deployment strategy, I can automate a lot of stuff and worry less about everything else. The app deploys automatically every time I push something to &lt;a href="https://github.com/jozsefsallai/webmongo"&gt;the master branch on GitHub&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Open-Source And Self-Hostable
&lt;/h2&gt;

&lt;p&gt;Still don't like the idea of using a MongoDB manager that's hosted on someone else's server? Don't worry! I won't get offended by that and I can totally understand you! WebMongo is &lt;strong&gt;completely open-source&lt;/strong&gt;, which is why &lt;strong&gt;you can easily deploy it yourself&lt;/strong&gt; anywhere you want.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;See the repo here:&lt;/strong&gt; &lt;a href="https://github.com/jozsefsallai/webmongo"&gt;https://github.com/jozsefsallai/webmongo&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;MongoDB is awesome, but we need a way to &lt;strong&gt;make it very easy to access our servers from anywhere&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Storing MongoDB credentials on a remote server is dangerous&lt;/strong&gt; because it can become a target for hackers.&lt;/li&gt;
&lt;li&gt;WebMongo is an &lt;strong&gt;open-source, fast, and secure web UI for MongoDB&lt;/strong&gt; that uses a different approach for this by &lt;strong&gt;saving all data on the user's computer&lt;/strong&gt; and using &lt;strong&gt;encrypted exporting/importing&lt;/strong&gt; as a &lt;strong&gt;sync&lt;/strong&gt; method.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;This project is something I'm really proud of and I'm looking forward to making it as great as I can. What are your thoughts, suggestions, questions, or bug reports? WebMongo is currently still in beta, so everyone's input would be greatly helpful! :) &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;You can access the production app here: &lt;a href="https://webmongo.now.sh"&gt;https://webmongo.now.sh&lt;/a&gt;.&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>showdev</category>
      <category>mongodb</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Creating Fast APIs In Go Using Fiber</title>
      <dc:creator>József Sallai</dc:creator>
      <pubDate>Tue, 07 Apr 2020 16:41:46 +0000</pubDate>
      <link>https://dev.to/jozsefsallai/creating-fast-apis-in-go-using-fiber-59m9</link>
      <guid>https://dev.to/jozsefsallai/creating-fast-apis-in-go-using-fiber-59m9</guid>
      <description>&lt;p&gt;During my career as a software engineer, I've made several APIs in different languages and frameworks. I've used Flask, Falcon, and Django (Python), Laravel (PHP), Rails (Ruby), but my all time favorite web framework is &lt;a href="https://expressjs.com/"&gt;Express (Node.js)&lt;/a&gt;. It's performant, flexible, and very fun to use. I also really like &lt;a href="https://koajs.com/"&gt;Koa&lt;/a&gt;, which is a successor to Express, made by the guys behind Express. I think it's safe to say that Express became the de facto standard web framework among Node.js developers.&lt;/p&gt;

&lt;p&gt;However, there's another programming language I absolutely love, and that is &lt;strong&gt;Go&lt;/strong&gt;. It's a performant, cross-platform, and fun language that I keep recommending to everyone who wants to learn a C-like language but doesn't feel ready for the complexity of C/C++ and the like. Go is mature enough to be used in production environments, which is why many companies - large and small - use it to develop their infrastructure.&lt;/p&gt;

&lt;p&gt;Just like in the case of most modern programming languages, Go also has lots of community-maintained web frameworks (such as Gin, Buffalo, or Fasthttp). However, the framework I've discovered recently and really like ever since is &lt;a href="https://github.com/gofiber/fiber"&gt;Fiber&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Fiber is a web framework that is very similar to Express, which makes it a perfect choice for any Node.js developer that decides to delve into the land of gophers. The framework is a well-designed wrapper around another framework - &lt;a href="https://github.com/valyala/fasthttp"&gt;Fasthttp&lt;/a&gt; - which is considered to be one of the fastest web frameworks written in Go. Overall, these are the main advantages of Fiber:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Express-like syntax&lt;/li&gt;
&lt;li&gt;Highly performant, thanks to the underlying Fasthttp framework&lt;/li&gt;
&lt;li&gt;Zero memory allocation&lt;/li&gt;
&lt;li&gt;Easy to learn, especially if you've used Express or Koa before&lt;/li&gt;
&lt;li&gt;Easy to write unit tests for, thanks to the built-in &lt;a href="https://fiber.wiki/application#test"&gt;Test method&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In this article, we're gonna build the API for a todo app using Fiber and &lt;a href="https://www.mongodb.com/"&gt;MongoDB&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;The very first thing we need to make sure of is that we have all our dependencies ready. We're of course going to need to have &lt;a href="https://golang.org/dl/"&gt;Go (1.11 or later)&lt;/a&gt; installed on our machine. We'll also need a MongoDB server running on our machine (or you could use &lt;a href="https://www.mongodb.com/cloud/atlas"&gt;MongoDB Atlas&lt;/a&gt;). Make sure to create a database called &lt;strong&gt;todos&lt;/strong&gt;. I'm not going to go into how to install and configure these, since it's pretty straightforward, so we can focus on the Go stuff!&lt;/p&gt;

&lt;p&gt;Once we have our stuff ready, we're ready to Go! Let's first create a directory for our project and initialize a Go module:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir &lt;/span&gt;tasks-api
&lt;span class="nb"&gt;cd &lt;/span&gt;tasks-api
go mod init github.com/yourusername/tasks-api
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once the module is ready, we need to install our project's Go dependencies. Since we've created a module, Go will add these dependencies to the &lt;code&gt;go.mod&lt;/code&gt; file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;go get github.com/gofiber/fiber
go get github.com/Kamva/mgm/v2
go get go.mongodb.org/mongo-driver
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first dependency is obviously the Fiber web framework, the second one is a MongoDB ODM for Go, and the third one is the official MongoDB driver for Go. Once these steps are done, we can start writing some code!&lt;/p&gt;

&lt;h2&gt;
  
  
  The Directory Structure
&lt;/h2&gt;

&lt;p&gt;Our todo API will have the following directory structure:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;.&lt;/span&gt;
├── controllers
│   └── TodoController.go
├── go.mod
├── go.sum
├── models
│   └── Todo.go
└── server.go

2 directories, 5 files
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;controllers&lt;/code&gt; directory will contain the controllers for each route or middleware that we're going to define in our Fiber app. For the sake of organization, it's recommended that you separate your controllers based on the model you're working with. This doesn't really make sense now, since we only have one model - the Todo model, but our application might scale in the future and include things like users and sessions.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;models&lt;/code&gt; directory contains the definitions of our models. As I said in the previous paragraph, our application currently only has one model, but if our app also had authentication/users, that would require a separate model.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;server.go&lt;/code&gt; is the main entry point of our application. This is where we define our routes, middlewares, initialize our MongoDB connection, and start the server. In larger applications, I'd split these steps in separate files, again, for the sake of organization, but I feel like that's not necessary for such a small application right now.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Todo Model
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/Kamva/mgm"&gt;MGM is a MongoDB object-document mapper&lt;/a&gt;, which means it contains object definitions and methods that make it easy to read and mutate data in our MongoDB database.&lt;/p&gt;

&lt;p&gt;Our Todo model should be straightforward: it should contain the title of the todo, a description, and a boolean that specifies whether the todo is done or still pending.&lt;/p&gt;

&lt;p&gt;In our &lt;code&gt;models&lt;/code&gt; folder, create a file called &lt;code&gt;Todo.go&lt;/code&gt;. Since this file is inside of the &lt;code&gt;models&lt;/code&gt; folder, it means that the name of the Go package it belongs to will be &lt;code&gt;models&lt;/code&gt;. Let's start by defining our package and importing the MGM dependency:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"github.com/Kamva/mgm/v2"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, we can specify and export the &lt;code&gt;Todo&lt;/code&gt; model by defining it as a struct-like type. Remember that in Go, a variable, type definition, or function will only be exported if it starts with a capital letter, so make sure to write &lt;code&gt;Todo&lt;/code&gt; and not &lt;code&gt;todo&lt;/code&gt; when defining your model!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;// Todo is the model that defines a todo entry&lt;/span&gt;
&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;Todo&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;mgm&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DefaultModel&lt;/span&gt; &lt;span class="s"&gt;`bson:",inline"`&lt;/span&gt;
    &lt;span class="n"&gt;Title&lt;/span&gt;            &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="s"&gt;`json:"title" bson:"title"`&lt;/span&gt;
    &lt;span class="n"&gt;Description&lt;/span&gt;      &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="s"&gt;`json:"description" bson:"description"`&lt;/span&gt;
    &lt;span class="n"&gt;Done&lt;/span&gt;             &lt;span class="kt"&gt;bool&lt;/span&gt;   &lt;span class="s"&gt;`json:"done" bson:"done"`&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At the beginning of the model's definition, we "inherit" from MGM's &lt;code&gt;DefaultModel&lt;/code&gt; interface, which allows us to use the methods and properties that apply to any MongoDB model (such as the &lt;code&gt;_id&lt;/code&gt;, &lt;code&gt;created_at&lt;/code&gt;, and &lt;code&gt;updated_at&lt;/code&gt; fields). Once that is done, we can define our fields.&lt;/p&gt;

&lt;p&gt;To make things a bit cleaner, we can write a wrapper function that creates a new Todo object for us. We can use something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;// CreateTodo is a wrapper that creates a new todo entry&lt;/span&gt;
&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;CreateTodo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;description&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Todo&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;Todo&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;Title&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;       &lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;Description&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;Done&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;        &lt;span class="no"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This function takes a title and a description as parameters, and returns a pointer to a Todo struct that contains the specified data. You can go very creative and customize these wrappers as your application scales.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Controllers
&lt;/h2&gt;

&lt;p&gt;This is where we define functions that we can use as fallbacks in the Fiber route and middleware definitions. Our app doesn't have any middlewares, so here's a list of all the routes that we'll need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;GET /api/todos&lt;/code&gt; - lists all todos in the database&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;GET /api/todos/:id&lt;/code&gt; - retrieves a todo based on its ID&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;POST /api/todos&lt;/code&gt; - creates a Todo object and adds it to the database&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;PATCH /api/todos/:id&lt;/code&gt; - changes the &lt;code&gt;Done&lt;/code&gt; property of a todo based on its ID&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;DELETE /api/todos/:id&lt;/code&gt; - deletes a todo based on its ID&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let's create a file called &lt;code&gt;TodoController.go&lt;/code&gt; inside of the &lt;code&gt;controllers&lt;/code&gt; directory and define the package as well as its imports:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;controllers&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"github.com/Kamva/mgm/v2"&lt;/span&gt;
    &lt;span class="s"&gt;"github.com/gofiber/fiber"&lt;/span&gt;
    &lt;span class="s"&gt;"github.com/yourusername/tasks-api/models"&lt;/span&gt;
    &lt;span class="s"&gt;"go.mongodb.org/mongo-driver/bson"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Don't forget to replace &lt;code&gt;github.com/yourusername/tasks-api&lt;/code&gt; with the name of your module (that you provided when you ran &lt;code&gt;go mod init&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;Now that we have that out of the way, we can start defining the controllers for our routes. Let's start with the &lt;code&gt;GET /api/todos&lt;/code&gt; route.&lt;/p&gt;

&lt;h3&gt;
  
  
  GET all todos
&lt;/h3&gt;

&lt;p&gt;Since every controller will be a callback function that we'll pass as a second parameter to our route definitions, we need to make sure our function works like what Fiber expects in a route definition. Our function accepts one parameter (the request context) and should not return anything:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;// GetAllTodos - GET /api/todos&lt;/span&gt;
&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;GetAllTodos&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;fiber&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Ctx&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c"&gt;// TODO: implement&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;(note: the comment above the function's name doesn't have to look like that, I just think it makes it easier to instantly know what controller does what)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;First we need to access our &lt;code&gt;todos&lt;/code&gt; collection. We can do using &lt;code&gt;mgm.Coll()&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;collection&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;mgm&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Coll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Todo&lt;/span&gt;&lt;span class="p"&gt;{})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then we need to initialize an empty array of type &lt;code&gt;models.Todo&lt;/code&gt; to store our todos in:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;todos&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Todo&lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we can use &lt;code&gt;collection.SimpleFind()&lt;/code&gt; to fetch all entries in the database. The function takes two parameters: the first parameter is the memory address of the variable in which the result should be stored and the second is a filter. If the filter is empty, it will return all entries.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;collection&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SimpleFind&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;todos&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;bson&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;D&lt;/span&gt;&lt;span class="p"&gt;{})&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;500&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fiber&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Map&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="s"&gt;"ok"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;    &lt;span class="no"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s"&gt;"error"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="c"&gt;// necessary, or else the controller will continue&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You'll notice that we've done some error checking. If for some reason, the MongoDB driver can't access the collection's items (i.e. connection issues, non-existing database), it will set the HTTP status of the response to 500 (Internal Server Error) and will return a JSON object that contains the error message returned by the driver. Notice how we used &lt;code&gt;fiber.Map&lt;/code&gt; to send a JSON response. This is very handy! Here's how the error handler would look like in Express:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can see how similar they are!&lt;/p&gt;

&lt;p&gt;Once the results have been stored, we can return it as JSON to the client:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fiber&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Map&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="s"&gt;"ok"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;    &lt;span class="no"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s"&gt;"todos"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;todos&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So our controller looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;// GetAllTodos - GET /api/todos&lt;/span&gt;
&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;GetAllTodos&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;fiber&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Ctx&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;collection&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;mgm&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Coll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Todo&lt;/span&gt;&lt;span class="p"&gt;{})&lt;/span&gt;
    &lt;span class="n"&gt;todos&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Todo&lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;

    &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;collection&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SimpleFind&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;todos&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;bson&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;D&lt;/span&gt;&lt;span class="p"&gt;{})&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;500&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fiber&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Map&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="s"&gt;"ok"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;    &lt;span class="no"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s"&gt;"error"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="p"&gt;})&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fiber&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Map&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="s"&gt;"ok"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;    &lt;span class="no"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s"&gt;"todos"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;todos&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  GET todo by ID
&lt;/h3&gt;

&lt;p&gt;Each MongoDB document has an automatically assigned, unique ID which we can use to fetch a specific document. Our route looks like this: &lt;code&gt;/api/todos/:id&lt;/code&gt;, and thanks to Fiber, we can easily implement a controller for it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;// GetTodoByID - GET /api/todos/:id&lt;/span&gt;
&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;GetTodoByID&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;fiber&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Ctx&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Params&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;todo&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Todo&lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;
    &lt;span class="n"&gt;collection&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;mgm&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Coll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;todo&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;collection&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FindByID&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;todo&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;404&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fiber&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Map&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="s"&gt;"ok"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;    &lt;span class="no"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s"&gt;"error"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"Todo not found."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;})&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fiber&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Map&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="s"&gt;"ok"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;   &lt;span class="no"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s"&gt;"todo"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;todo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It uses the &lt;code&gt;ctx.Params()&lt;/code&gt; method to find the value of the &lt;code&gt;:id&lt;/code&gt; parameter and searches in the collection using that ID. If it fails (i.e. doesn't find a document with that ID), it will return a JSON response telling you so, with a 404 HTTP status code.&lt;/p&gt;

&lt;h3&gt;
  
  
  POST todo to server
&lt;/h3&gt;

&lt;p&gt;When &lt;code&gt;POST&lt;/code&gt;ing stuff, there's one question that often crosses the mind of a web developer. How should I accept the incoming data? Using a form? A urlencoded body? JSON? Perhaps, raw text? Well, since we're talking about a REST API, the convention is to accept and send JSON data. Fiber makes it very easy to handle any kind of data however, depending on the &lt;code&gt;Content-Type&lt;/code&gt; header:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;application/json&lt;/code&gt; - parses the body as a JSON object&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;application/xml&lt;/code&gt; - parses an XML document&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;application/x-www-form-urlencoded&lt;/code&gt; - parses a urlencoded string in the body&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;multipart/form-data&lt;/code&gt; - parses a good old form&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We want to create a new struct that contains the parameters we need to extract from the request's body. We only need the title and the description of the todo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;params&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="nb"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Title&lt;/span&gt;       &lt;span class="kt"&gt;string&lt;/span&gt;
    &lt;span class="n"&gt;Description&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will be empty at first, so it will contain two empty strings. We can then use Fiber's &lt;code&gt;ctx.BodyParser()&lt;/code&gt; method to parse the request's body and bind it to our &lt;code&gt;params&lt;/code&gt; variable:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;BodyParser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can then check if both parameters were provided, and if any of them is missing, we can return an error with an HTTP status code of 400 (Bad Request):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Title&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Description&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;400&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fiber&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Map&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="s"&gt;"ok"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;    &lt;span class="no"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s"&gt;"error"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"Title or description not specified."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once that's done, we can create our todo using our helper function and then return it to the client:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;todo&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CreateTodo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Description&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;mgm&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Coll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;todo&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;todo&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;500&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fiber&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Map&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="s"&gt;"ok"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;    &lt;span class="no"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s"&gt;"error"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fiber&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Map&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="s"&gt;"ok"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;   &lt;span class="no"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s"&gt;"todo"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;todo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here's the controller in its entirety:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;// CreateTodo - POST /api/todos&lt;/span&gt;
&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;CreateTodo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;fiber&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Ctx&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;params&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="nb"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;Title&lt;/span&gt;       &lt;span class="kt"&gt;string&lt;/span&gt;
        &lt;span class="n"&gt;Description&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;

    &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;BodyParser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Title&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Description&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;400&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fiber&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Map&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="s"&gt;"ok"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;    &lt;span class="no"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s"&gt;"error"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"Title or description not specified."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;})&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;todo&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CreateTodo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Description&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;mgm&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Coll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;todo&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;todo&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;500&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fiber&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Map&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="s"&gt;"ok"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;    &lt;span class="no"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s"&gt;"error"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="p"&gt;})&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fiber&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Map&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="s"&gt;"ok"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;   &lt;span class="no"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s"&gt;"todo"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;todo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  PATCH a todo to change its status
&lt;/h3&gt;

&lt;p&gt;The general idea behind the &lt;code&gt;PATCH /api/todos/:id&lt;/code&gt; route is to get the todo by ID, change the value of the &lt;code&gt;Done&lt;/code&gt; property to the opposite of the current value, save the document, and then return the modified document to the user.&lt;/p&gt;

&lt;p&gt;You can pretty much copy and paste the contents of the &lt;code&gt;GetTodoByID()&lt;/code&gt; controller and add this before the last step:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;todo&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Done&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;todo&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Done&lt;/span&gt;

&lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;collection&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;todo&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;500&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fiber&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Map&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="s"&gt;"ok"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;    &lt;span class="no"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s"&gt;"error"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here's the final controller:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;// ToggleTodoStatus - PATCH /api/todos/:id&lt;/span&gt;
&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;ToggleTodoStatus&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;fiber&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Ctx&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Params&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;todo&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Todo&lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;
    &lt;span class="n"&gt;collection&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;mgm&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Coll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;todo&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;collection&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FindByID&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;todo&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;404&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fiber&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Map&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="s"&gt;"ok"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;    &lt;span class="no"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s"&gt;"error"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"Todo not found."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;})&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;todo&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Done&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;todo&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Done&lt;/span&gt;

    &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;collection&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;todo&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;500&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fiber&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Map&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="s"&gt;"ok"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;    &lt;span class="no"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s"&gt;"error"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="p"&gt;})&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fiber&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Map&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="s"&gt;"ok"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;   &lt;span class="no"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s"&gt;"todo"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;todo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  DELETE a todo
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;DELETE /api/todos/:id&lt;/code&gt; controller is also very similar, I don't think it needs any special explanation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;// DeleteTodo - DELETE /api/todos/:id&lt;/span&gt;
&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;DeleteTodo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;fiber&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Ctx&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Params&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;todo&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Todo&lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;
    &lt;span class="n"&gt;collection&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;mgm&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Coll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;todo&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;collection&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FindByID&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;todo&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;404&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fiber&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Map&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="s"&gt;"ok"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;    &lt;span class="no"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s"&gt;"error"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"Todo not found."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;})&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;collection&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Delete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;todo&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;500&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fiber&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Map&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="s"&gt;"ok"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;    &lt;span class="no"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s"&gt;"error"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="p"&gt;})&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fiber&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Map&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="s"&gt;"ok"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;   &lt;span class="no"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s"&gt;"todo"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;todo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And so, our controllers are done! We can move forward to the final step.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Server
&lt;/h2&gt;

&lt;p&gt;Like I said earlier, &lt;code&gt;server.go&lt;/code&gt; will initialize the MongoDB driver and the MGM ODM, define the routes of the app, and start the server. First, let's define the package and its imports:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"log"&lt;/span&gt;
    &lt;span class="s"&gt;"os"&lt;/span&gt;

    &lt;span class="s"&gt;"github.com/Kamva/mgm/v2"&lt;/span&gt;
    &lt;span class="s"&gt;"github.com/gofiber/fiber"&lt;/span&gt;
    &lt;span class="s"&gt;"github.com/jozsefsallai/fiber-todo-demo/controllers"&lt;/span&gt;
    &lt;span class="s"&gt;"go.mongodb.org/mongo-driver/mongo/options"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we need to define an &lt;code&gt;init()&lt;/code&gt; function in which we configure the MongoDB driver. This function will ever only be called once and will run before &lt;code&gt;main()&lt;/code&gt;, so we can have all our stuff ready here.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;init&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;connectionString&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"MONGODB_CONNECTION_STRING"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;connectionString&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;connectionString&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"mongodb://localhost:27017"&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;mgm&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SetDefaultConfig&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"todos"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Client&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ApplyURI&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;connectionString&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Fatal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We are going to use the &lt;code&gt;MONGODB_CONNECTION_STRING&lt;/code&gt; environment variable to specify the connection string used to connect to the MongoDB server and database. If there is no such environment variable, it will default to the local instance of MongoDB.&lt;/p&gt;

&lt;p&gt;Since we've already imported all of our controllers, we can just refer to them when defining our routes in the &lt;code&gt;main()&lt;/code&gt; function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;fiber&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;New&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/api/todos"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;controllers&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetAllTodos&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/api/todos/:id"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;controllers&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetTodoByID&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/api/todos"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;controllers&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CreateTodo&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Patch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/api/todos/:id"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;controllers&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ToggleTodoStatus&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Delete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/api/todos/:id"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;controllers&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DeleteTodo&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Listen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;3000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Easy as that! As you can see from this example as well, Fiber really does look like Express, which is a nice touch! By now you should be ready to start your application. Run the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;go run server.go
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And your server should be listening on port 3000.&lt;/p&gt;

&lt;h2&gt;
  
  
  Trying Out Your API
&lt;/h2&gt;

&lt;p&gt;You can use a tool such as &lt;a href="https://insomnia.rest"&gt;&lt;strong&gt;Insomnia&lt;/strong&gt;&lt;/a&gt; to test your API. First, let's test our &lt;code&gt;POST /api/todos&lt;/code&gt; endpoint:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--LNkDpgBR--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/j02tgxbfq9da2xoshvbf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--LNkDpgBR--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/j02tgxbfq9da2xoshvbf.png" alt="insomnia-1.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now that we have data, we can test the other endpoints as well. Here's what the output for &lt;code&gt;GET /api/todos&lt;/code&gt; looks like. You can see that it contains an array of all todos:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--OUHULkXR--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/61trug8l6aztsp7kemqt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--OUHULkXR--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/61trug8l6aztsp7kemqt.png" alt="insomnia-2.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We can also look for that specific ID and return its data:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--brCxyrwj--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/yphd270pa60u835e3rts.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--brCxyrwj--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/yphd270pa60u835e3rts.png" alt="insomnia-3.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And we can try changing its status:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Ww9nt2-q--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/cxal1httuj4sjzpdku8y.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Ww9nt2-q--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/cxal1httuj4sjzpdku8y.png" alt="insomnia-4.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;...or maybe even getting rid of it :(&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--QrFGL9uQ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/zylrnu28lc6c0a5dosg1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--QrFGL9uQ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/zylrnu28lc6c0a5dosg1.png" alt="insomnia-5.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;You can see how easy it is to create a REST API in Fiber, so why not go ahead and make something awesome? Or maybe even rewrite one of your existing projects to make use of Go's awesome speed and features? 👀&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;You can find the app we made in this article &lt;a href="https://github.com/jozsefsallai/fiber-todo-demo"&gt;on GitHub&lt;/a&gt;.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;This post was originally published on &lt;a href="https://blog.sallai.me/rest-api-with-fiber/"&gt;my blog&lt;/a&gt;.&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>fiber</category>
      <category>go</category>
      <category>api</category>
    </item>
    <item>
      <title>How I've Set Up My Windows Machine As A Development Environment (Without WSL)</title>
      <dc:creator>József Sallai</dc:creator>
      <pubDate>Tue, 14 Jan 2020 22:23:20 +0000</pubDate>
      <link>https://dev.to/jozsefsallai/how-i-ve-set-up-my-windows-machine-as-a-development-environment-without-wsl-2kcl</link>
      <guid>https://dev.to/jozsefsallai/how-i-ve-set-up-my-windows-machine-as-a-development-environment-without-wsl-2kcl</guid>
      <description>&lt;p&gt;&lt;em&gt;(Edit: while this tutorial is still very usable, I no longer use this setup. I've switched to using WSL2 exclusively and it works so much better than WSL1. I recommend you do the same 😊.)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Microsoft's operating system was an important part of my childhood. I grew up using Windows XP, which I really loved to hack to death. At the time, I didn't really know a whole lot about software development and I barely had any resources to learn anything about it since I didn't have internet. I used to have a book on Delphi which is what initially got me interested into writing programs.&lt;/p&gt;

&lt;p&gt;This... was a long time ago. After a while, I got more interested into web development and started making static HTML and CSS sites in Notepad++ on my Windows 7 machine. Back in the days, I was scared of Linux. I thought it would be too advanced of an operating system for me. Which might bring the question...&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Not Just Use Linux?
&lt;/h2&gt;

&lt;p&gt;I love Linux. It's an amazing operating system with a lot of great potential. I used to dual-boot Debian and Windows at some point, so no, I don't use Windows because I don't like Linux or don't know how to use it. I just have a lot more set of tools on Windows now compared to Linux, especially since I work in other domains too, which require me to have apps that don't exist on Linux (such as Adobe products, Ableton, etc.). I also really enjoy gaming and while I do think playing games on Linux is not &lt;em&gt;that&lt;/em&gt; bad, I still feel like it's a little more convenient to do it on Windows.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;So why do I just not dual boot?&lt;/strong&gt; I just... don't really feel the need of dual-booting right now. I have all the tools I need on my Windows machine and if I happen to need something that doesn't exist on Windows, I can just use WSL. It's good enough. Dual-booting is tedious and, for someone who needs a lot of disk space for audio and video stuff, it's just space in excess.&lt;/p&gt;

&lt;h2&gt;
  
  
  Today's Windows Isn't The Past's Windows
&lt;/h2&gt;

&lt;p&gt;Windows had been known among developers to be a very developer-unfriendly operating system for anything that doesn't involve developing for Windows specifically. Today, however, Windows 10 has made a lot of advancements regarding developer experience. I think we can safely say that today's Windows -- heck, even today's Microsoft -- is developer-friendly.&lt;/p&gt;

&lt;p&gt;Love them or hate them, I feel like I'm not the only one who thinks that Microsoft has finally started to embrace the developer community, which is a good change in my opinion.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Not Use WSL?
&lt;/h2&gt;

&lt;p&gt;WSL is an awesome addition to Windows and I'm glad that Windows ships with a full-fledged Linux kernel now. It's definitely a good step forward. But why do I not use WSL?&lt;/p&gt;

&lt;p&gt;It might be just me and the types of tasks I perform, but WSL hangs quite often, especially during Webpack frontend builds. I would love to use WSL more often, but this constant hanging just doesn't really do it for me. &lt;em&gt;(If anyone has suggestions on how to fix this, do let me know though!)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Edit: this is no longer the case in WSL2. It works amazingly and never hanged for me so far.&lt;/strong&gt; Though some people might not like the idea of running what's basically a stripped-down Linux virtual machine.&lt;/p&gt;

&lt;h2&gt;
  
  
  So, What's My Developer Environment Setup Look Like?
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Programming Languages and Frameworks
&lt;/h3&gt;

&lt;p&gt;Luckily for me, the languages and frameworks I mostly use run great on every platform, including Windows. So all you really have to do is download the Windows release of these tools. I do most of my work with Node.js and in about 96% of the cases I had no trouble working with it under Windows (and when something doesn't work, I check what's wrong and try to fix it myself). Same goes for Go and Dart.&lt;/p&gt;

&lt;p&gt;I sometimes face issues when I use Python libraries that depend on a library that can only be found on UNIX-like operating systems. But this is not a big issue for me because most of the Python stuff I do works out of the box on Windows too.&lt;/p&gt;

&lt;p&gt;I know I'll probably get a lot of hate for this but when it comes to C++, I use my MinGW setup from ages ago that I've installed for school work. It works well, so I have no reason to complain.&lt;/p&gt;

&lt;p&gt;PHP on the other hand, is a little complicated. As you may know, PHP requires that you have a web server backing it. Luckily I don't use PHP much, but for times when I &lt;em&gt;have&lt;/em&gt; to use PHP, I have a custom nginx + MariaDB + PHP + Redis setup that I made and will talk about in a bit.&lt;/p&gt;

&lt;h3&gt;
  
  
  Tools
&lt;/h3&gt;

&lt;p&gt;Here are the developer tools I have installed on my computer:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://github.com/git-for-windows/git/releases/"&gt;Git for Windows&lt;/a&gt;&lt;/strong&gt; - allows me to use git and a lot of useful Linux commands such as &lt;code&gt;ssh&lt;/code&gt;, &lt;code&gt;ssh-keygen&lt;/code&gt;, &lt;code&gt;ls&lt;/code&gt;, etc., and most importantly, &lt;code&gt;bash&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://code.visualstudio.com/"&gt;Visual Studio Code&lt;/a&gt;&lt;/strong&gt; - an excellent code editor made by Microsoft which has never failed me and I consider one of my best friends. For smaller edits, I use &lt;code&gt;nano&lt;/code&gt; or &lt;code&gt;vim&lt;/code&gt; that comes with Git for Windows&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://cmder.net/"&gt;Cmder Mini&lt;/a&gt;&lt;/strong&gt; - this is still my favorite terminal emulator for Windows. It has many awesome and useful features, it's fast, and it looks good. Here's my Cmder setup: &lt;a href="https://gist.github.com/jozsefsallai/acd53ea9f4990a1d9c39479fbe6dec6e"&gt;https://gist.github.com/jozsefsallai/acd53ea9f4990a1d9c39479fbe6dec6e&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://insomnia.rest/"&gt;Insomnia&lt;/a&gt;&lt;/strong&gt; - slick, fast, and feature-rich alternative to Postman&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://scoop.sh/"&gt;scoop&lt;/a&gt;&lt;/strong&gt; - a package manager for Windows, mainly geared towards developer tools&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="http://nginx.org/en/download.html"&gt;nginx&lt;/a&gt; (with some custom scripts)&lt;/strong&gt; - fast web server that I mainly use for PHP projects or as a reverse proxy&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://downloads.mariadb.org/"&gt;MariaDB&lt;/a&gt;&lt;/strong&gt; - community-maintained fork of MySQL. In my experience it works very well on Windows.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://www.memurai.com/"&gt;Memurai&lt;/a&gt;&lt;/strong&gt; - Windows alternative to Redis. There used to be a Redis client for Windows at some point but I couldn't find a version that's up to date. Memurai works pretty well.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Have Your PATH Sorted Out Well
&lt;/h3&gt;

&lt;p&gt;Windows 10 offers an intuitive way to edit your PATH environment variable. Just go to &lt;strong&gt;Search&lt;/strong&gt;, type "environment variables", and select one of the options that pop up to easily edit all your variables, including PATH.&lt;/p&gt;

&lt;p&gt;Once you have the right binaries in your PATH, you are unstoppable... mostly.&lt;/p&gt;

&lt;p&gt;This is what my system-level PATH looks like:&lt;/p&gt;


&lt;center&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--PHwy-g5L--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/7j0zzzyeckxo350j3wfi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--PHwy-g5L--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/7j0zzzyeckxo350j3wfi.png" alt="System-level PATH"&gt;&lt;/a&gt;&lt;/p&gt;


&lt;/center&gt;

&lt;p&gt;...and this is my user-level PATH:&lt;/p&gt;


&lt;center&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--uDj7eHWB--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/w9xtf68tmg1oyfqjbtw5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--uDj7eHWB--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/w9xtf68tmg1oyfqjbtw5.png" alt="User-level PATH"&gt;&lt;/a&gt;&lt;/p&gt;


&lt;/center&gt;
&lt;h3&gt;
  
  
  About That PHP Thing...
&lt;/h3&gt;

&lt;p&gt;I said it's complicated but if you have your PATH sorted out well, it's really not that bad. First I make sure that MariaDB and PHP are accessible from the PATH. Then, after downloading the Windows release of nginx, I create a folder called &lt;code&gt;windows-bin&lt;/code&gt;. Inside this folder, I place two files:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Warning: I am bad at batch and VB scripts!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;File 1 - &lt;code&gt;nginx.bat&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight batchfile"&gt;&lt;code&gt;@echo &lt;span class="na"&gt;off&lt;/span&gt;

&lt;span class="kd"&gt;set&lt;/span&gt; &lt;span class="kd"&gt;currentDirectory&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;%cd%&lt;/span&gt;

&lt;span class="nb"&gt;cd&lt;/span&gt; &lt;span class="kd"&gt;C&lt;/span&gt;:\dev\nginx
&lt;span class="kd"&gt;nginx&lt;/span&gt; &lt;span class="err"&gt;%&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt; &lt;span class="err"&gt;%&lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;

&lt;span class="nb"&gt;cd&lt;/span&gt; &lt;span class="nv"&gt;%currentDirectory%&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Where &lt;code&gt;C:\dev\nginx&lt;/code&gt; is the path to your nginx directory. The reason I have to do this is that adding nginx to the PATH is NOT GOOD as &lt;code&gt;nginx.exe&lt;/code&gt; will literally only work if you run it from nginx's folder. It's so weird, but this is a workaround.&lt;/p&gt;

&lt;p&gt;File 2 - &lt;code&gt;nginx-utils.bat&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight batchfile"&gt;&lt;code&gt;@echo &lt;span class="na"&gt;off&lt;/span&gt;

&lt;span class="kd"&gt;set&lt;/span&gt; &lt;span class="kd"&gt;currentDirectory&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;%cd%&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="err"&gt;%&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="o"&gt;[]&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;
  &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="kd"&gt;nginx&lt;/span&gt;&lt;span class="na"&gt;-utils&lt;/span&gt;: &lt;span class="kd"&gt;Please&lt;/span&gt; &lt;span class="kd"&gt;provide&lt;/span&gt; &lt;span class="kd"&gt;a&lt;/span&gt; &lt;span class="kd"&gt;parameter&lt;/span&gt;.
  &lt;span class="k"&gt;goto&lt;/span&gt; &lt;span class="nl"&gt;:EOF&lt;/span&gt;
&lt;span class="o"&gt;)&lt;/span&gt;

&lt;span class="nb"&gt;cd&lt;/span&gt; &lt;span class="s2"&gt;"C:\dev\nginx"&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="err"&gt;%&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;start&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;
  &lt;span class="nb"&gt;wscript&lt;/span&gt; &lt;span class="s2"&gt;"autostart.vbs"&lt;/span&gt;
&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;
  &lt;span class="nb"&gt;wscript&lt;/span&gt; &lt;span class="s2"&gt;"autostart.vbs"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="err"&gt;%&lt;/span&gt;&lt;span class="s2"&gt;1"&lt;/span&gt;
&lt;span class="o"&gt;)&lt;/span&gt;

&lt;span class="nb"&gt;cd&lt;/span&gt; &lt;span class="nv"&gt;%currentDirectory%&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, the other file is just some extra commands that I wrote to make it easier to work with nginx and PHP. Again, make sure to change &lt;code&gt;C:\dev\nginx&lt;/code&gt; to your nginx directory.&lt;/p&gt;

&lt;p&gt;Inside the nginx directory, I also have an &lt;code&gt;autostart.vbs&lt;/code&gt; file that I have added to my Startup folder so that nginx and PHP can start when I turn my computer on. It also allows me to run nginx in the background without having to keep an extra console window open. This is the contents of the file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight vb"&gt;&lt;code&gt;&lt;span class="k"&gt;Set&lt;/span&gt; &lt;span class="n"&gt;TaskRunner&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;WScript&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CreateObject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"WScript.Shell"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;NGINXDIR&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"C:\dev\nginx"&lt;/span&gt;
&lt;span class="n"&gt;PHPDIR&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"C:\dev\php"&lt;/span&gt;

&lt;span class="n"&gt;if&lt;/span&gt; &lt;span class="n"&gt;WScript&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Arguments&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="n"&gt;then&lt;/span&gt;
  &lt;span class="n"&gt;TaskRunner&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CurrentDirectory&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;NGINXDIR&lt;/span&gt;
  &lt;span class="n"&gt;TaskRunner&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Run&lt;/span&gt; &lt;span class="s"&gt;"nginx.exe"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;False&lt;/span&gt;
  &lt;span class="n"&gt;TaskRunner&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CurrentDirectory&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;PHPDIR&lt;/span&gt;
  &lt;span class="n"&gt;TaskRunner&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Run&lt;/span&gt; &lt;span class="s"&gt;"php-cgi -b 127.0.0.1:9000 -c php.ini"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;False&lt;/span&gt;
&lt;span class="n"&gt;else&lt;/span&gt;
  &lt;span class="n"&gt;if&lt;/span&gt; &lt;span class="n"&gt;WScript&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Arguments&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"stop"&lt;/span&gt; &lt;span class="n"&gt;then&lt;/span&gt;
    &lt;span class="n"&gt;TaskRunner&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Run&lt;/span&gt; &lt;span class="s"&gt;"taskkill /F /IM nginx.exe"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;False&lt;/span&gt;
    &lt;span class="n"&gt;TaskRunner&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Run&lt;/span&gt; &lt;span class="s"&gt;"taskkill /F /IM php-cgi.exe"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;False&lt;/span&gt;
  &lt;span class="n"&gt;elseif&lt;/span&gt; &lt;span class="n"&gt;WScript&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Arguments&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"reload-php"&lt;/span&gt; &lt;span class="n"&gt;then&lt;/span&gt;
    &lt;span class="n"&gt;TaskRunner&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Run&lt;/span&gt; &lt;span class="s"&gt;"taskkill /F /IM php-cgi.exe"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;False&lt;/span&gt;
    &lt;span class="n"&gt;WScript&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Sleep&lt;/span&gt; &lt;span class="mi"&gt;2000&lt;/span&gt;
    &lt;span class="n"&gt;TaskRunner&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CurrentDirectory&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;PHPDIR&lt;/span&gt;
    &lt;span class="n"&gt;TaskRunner&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Run&lt;/span&gt; &lt;span class="s"&gt;"php-cgi -b 127.0.0.1:9000 -c php.ini"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;False&lt;/span&gt;
  &lt;span class="n"&gt;else&lt;/span&gt;
    &lt;span class="n"&gt;MsgBox&lt;/span&gt; &lt;span class="s"&gt;"Invalid parameter: "&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;WScript&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Arguments&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="s"&gt;" Accepted parameters: stop, reload-php"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Invalid Parameter"&lt;/span&gt;
  &lt;span class="n"&gt;end&lt;/span&gt; &lt;span class="n"&gt;if&lt;/span&gt;
&lt;span class="n"&gt;end&lt;/span&gt; &lt;span class="n"&gt;if&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Why don't you just use XAMPP?&lt;/strong&gt;&lt;br&gt;
I don't like it :) that, and I can manage my projects better if I have a custom setup.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Development on Windows is really not as bad as it once used to be and I feel like people who don't want to have an extra OS on their computer should try it out. Check if you can work with your tools and frameworks on Windows and make a decision afterwards. But don't forget - Linux (or Mac) will always be a better choice for development! So if you don't have to use programs that are not available on Linux or if you don't play many video games, you should probably stick to Linux (or Mac)!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;This post was originally published on &lt;a href="https://blog.sallai.me/my-windows-development-environment/"&gt;my blog&lt;/a&gt;.&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>windows</category>
      <category>setup</category>
    </item>
  </channel>
</rss>
