Tag: Should

Container Units Should Be Pretty Handy

Container queries are going to solve this long-standing issue in web design where we want to make design choices based on the size of an element (the container) rather than the size of the entire page. So, if a container is 600px wide, perhaps it has a row-like design, but any narrower than that it has a column-like design, and we’ll have that kind of control. That’s much different than transitioning between layouts based on screen size.

We can already size some things based on the size of an element, thanks to the % unit. For example, all these containers are 50% as wide as their parent container.

The % here is 1-to-1 with the property in use, so width is a % of width. Likewise, I could use % for font-size, but it will be a % of the parent container’s font-size. There is nothing that lets me cross properties and set the font-size as a % of a container’s width.

That is, unless we get container units! Here’s the table of units per the draft spec:

unit relative to
qw 1% of a query container’s width
qh 1% of a query container’s height
qi 1% of a query container’s inline size
qb 1% of a query container’s block size
qmin The smaller value of qi or qb
qmax The larger value of qi or qb

With these, I could easily set the font-size to a percentage of the parent container’s width. Or line-height! Or gap! Or margin! Or whatever!

Miriam notes that we can actually play with these units right now in Chrome Canary, as long as the container queries flag is on.

I had a quick play too. I’ll just put a video here as that’ll be easier to see in these super early days.

And some great exploratory work from Scott here as well:

Ahmad Shadeed is also all over this!

Query units can save us effort and time when dealing with things like font-sizepadding, and margin within a component. Instead of manually increasing the font size, we can use query units instead.

Ahmad Shadeed, “CSS Container Query Units”

Maybe container queries and container units will drop for real at the same time. 🤷

The post Container Units Should Be Pretty Handy appeared first on CSS-Tricks. You can support CSS-Tricks by being an MVP Supporter.


, , , ,

Scaling Organizations Should Consider Building a Website Backed by a CRM Platform

To make some terminology clear here:

  • CMS = Content Management System
  • CRM = Customer Relationship Management

Both are essentially database-backed systems for managing data. HubSpot is both, and much more. Where a CMS might be very focused on content and the metadata around making content useful, a CRM is focused on leads and making communicating with current and potential customers easier.

They can be brothers-in-arms. We’ll get to that.

Say a CRM is set up for people. You run a Lexus dealership. There is a quote form on the website. People fill it out and enter the CRM. That lead can go to your sales team for taking care of that customer.

But a CRM could be based on other things. Say instead of people it’s based on real estate listings. Each main entry is a property, with essentially metadata like photos, address, square footage, # of bedrooms/baths, etc. Leads can be associated with properties.

That would be a nice CRM setup for a real estate agency, but the data that is in that CRM might be awfully nice for literally building a website around those property listings. Why not tap into that CRM data as literal data to build website pages from?

That’s what I mean by a CRM and CMS being brothers-in-arms. Use them both! That’s why HubSpot can be an ideal home for websites like this.

To keep that tornado of synergy going, HubSpot can also help with marketing, customer service, and integrations. So there is a lot of power packed into one platform.

And with that power, also a lot of comfort and flexibility.

  • You’re still developing locally.
  • You’re still using Git.
  • You can use whatever framework or site-building tools you want.
  • You’ve got a CLI to control things.
  • There is a VS Code Extension for super useful auto-complete of your data.
  • There is a staging environment.

And the feature just keep coming. HubSpot really has a robust set of tools to make sure you can do what you need to do.

As developer-rich as this all is, it doesn’t mean that it’s developer-only. There are loads of tools for working with the website you build that require no coding at all. Dashboard for content management, data wrangling, style control, and even literal drag-and-drop page builders.

It’s all part of a very learnable system.

Themestemplatesmodules, and fields are the objects you’ll work with most in HubSpot CMS as a developer. Using these different objects effectively lets you give content creators the freedom to work and iterate on websites independently while staying inside style and layout guardrails you set.

The post Scaling Organizations Should Consider Building a Website Backed by a CRM Platform appeared first on CSS-Tricks. You can support CSS-Tricks by being an MVP Supporter.


, , , , , , ,

Should DevTools teach the CSS cascade?

Stefan Judis, two days before I mouthed off about using (X, X, X, X) for talking about specificity, has a great blog post not only using that format, but advocating that browser DevTools should show us that value by selectors.

I think that the above additions could help to educate developers about CSS tremendously. The only downside I can think of is that additional information might overwhelm developers, but I would take that risk in favor of more people learning CSS properly.

I’d be for it. The crossed-off UI for the “losing” selectors is attempting to teach this, but without actually teaching it. I wouldn’t be that worried about the information being overwhelming. I think if they are considerate about the design, it can be done tastefully. DevTools is a very information-dense place anyway.

Direct Link to ArticlePermalink

The post Should DevTools teach the CSS cascade? appeared first on CSS-Tricks.

You can support CSS-Tricks by being an MVP Supporter.


, , ,

HTML Video Sources Should Be Responsive

Scott Jehl doesn’t mince words here:

Removing media support from HTML video was a mistake. It means that for every video we embed in HTML, we’re stuck with the choice of serving source files that are potentially too large or small for many users’ devices (resulting in poor performance, wasteful data consumption, and even sub-optimal quality on larger screens), or resorting to more complicated server-side or scripted or third-party solutions to deliver a correct size.

I remember when responsive images were just starting to come out. One way to explain it was to say it’s like <video> in that you can have multiple <source> elements inside which (in supporting browsers) allowed you to specify attributes like type (e.g. video format) and media (e.g. screen size). But then

Despite being implemented in multiple browsers, the feature was removed from browsers and the HTML specification, without any proposed replacement for the functionality it once provided. One exception is the feature was never removed from Webkit, so it still works in Safari browsers, which is great.

I don’t remember that. That feels like a big WTF moment (some background). I think of the web as being tremendous at backwards compatibility. It’s a rare day when we just yank stuff, and even more rare is a yanking with no alternative whatsoever.

So now with responsive images being a success (it’s a success, right? I can’t imagine how incredibly much bandwidth it has saved the world)… can’t we… put it back?

When I have an immediate need for this, I always think of Cloudinary, because I can alter the size and format of video by changing the URL. Like here’s a video URL where the video codec is automatically determined and the size is forced down to 400px:


It’s nice to have tools like this, but that doesn’t mean the platform shouldn’t be helping.

Direct Link to ArticlePermalink

The post HTML Video Sources Should Be Responsive appeared first on CSS-Tricks.

You can support CSS-Tricks by being an MVP Supporter.


, , , ,

Why you should hire a front-end developer

Matt Hobbs says you should hire a front-end developer because…

  • “A front-end developer is the best person to champion accessibility best practices in product teams.”
  • “80-90% of the end-user response time is spent on the front end.”
  • “A front-end developer takes pressure off interaction designers.”
  • “If you do not have a front-end developer there is a high risk that the good work the rest of the team does will not be presented to users in the best way.”

Direct Link to ArticlePermalink

The post Why you should hire a front-end developer appeared first on CSS-Tricks.

You can support CSS-Tricks by being an MVP Supporter.


, , ,

Consistent Backends and UX: Why Should You Care?

Article Series

  1. Why should you care?
  2. What can go wrong? (Coming soon)
  3. What are the barriers to adoption? (Coming soon)
  4. How do new algorithms help? (Coming soon)

More than ever, new products aim to make an impact on a global scale, and user experience is rapidly becoming the determining factor for whether they are successful or not. These properties of your application can significantly influence the user experience:

  1. Performance & low latency
  2. The application does what you expect
  3. Security
  4. Features and UI

Let’s begin our quest toward the perfect user experience!

1) Performance & Low Latency

Others have said it before; performance is user experience (1, 2). When you have caught the attention of potential visitors, a slight increase in latency can make you lose that attention again. 

2) The application does what you expect

What does ‘does what you expect’ even mean? It means that if I change my name in my application to ‘Robert’ and reload the application, my name will be Robert and not Brecht. It seems important that an application delivers these guarantees, right? 

Whether the application can deliver on these guarantees depends on the database. When pursuing low latency and performance, we end up in the realm of distributed databases where only a few of the more recent databases deliver these guarantees. In the realm of distributed databases, there might be dragons, unless we choose a strongly (vs. eventually) consistent database. In this series, we’ll go into detail on what this means, which databases provide this feature called strong consistency, and how it can help you build awesomely fast apps with minimal effort.  

3) Security

Security does not always seem to impact user experience at first. However, as soon as users notice security flaws, relationships can be damaged beyond repair. 

4) Features and UI 

Impressive features and great UI have a great impact on the conscious and unconscious mind. Often, people only desire a specific product after they have experienced how it looks and feels. 

If a database saves time in setup and configuration, then the rest of our efforts can be focused on delivering impressive features and a great UI. There is good news for you; nowadays, there are databases that deliver on all of the above, do not require configuration or server provisioning, and provide easy to use APIs such as GraphQL out-of-the-box.

What is so different about this new breed of databases? Let’s take a step back and show how the constant search for lower latency and better UX, in combination with advances in database research, eventually led to a new breed of databases that are the ideal building blocks for modern applications. 

The Quest for distribution  

I. Content delivery networks

As we mentioned before, performance has a significant impact on UX. There are several ways to improve latency, where the most obvious is to optimize your application code. Once your application code is quite optimal, network latency and write/read performance of the database often remain the bottleneck. To achieve our low latency requirement, we need to make sure that our data is as close to the client as possible by distributing the data globally. We can deliver the second requirement (write/read performance) by making multiple machines work together, or in other words, replicating data.

Distribution leads to better performance and consequently to good user experience. We’ve already seen extensive use of a distribution solution that speeds up the delivery of static data; it’s called a Content Delivery Network (CDN). CDNs are highly valued by the Jamstack community to reduce the latency of their applications. They typically use frameworks and tools such as Next.js/Now, Gatsby, and Netlify to preassemble front end React/Angular/Vue code into static websites so that they can serve them from a CDN. 

Unfortunately, CDNs aren’t sufficient for every use case, because we can’t rely on statically generated HTML pages for all applications. There are many types of highly dynamic applications where you can’t statically generate everything. For example:

  1. Applications that require real-time updates for instantaneous communication between users (e.g., chat applications, collaborative drawing or writing, games).
  2. Applications that present data in many different forms by filtering, aggregating, sorting, and otherwise manipulating data in so many ways that you can’t generate everything in advance. 

II. Distributed databases

In general, a highly dynamic application will require a distributed database to improve performance. Just like a CDN, a distributed database also aims to become a global network instead of a single node. In essence, we want to go from a scenario with a single database node… 

…to a scenario where the database becomes a network. When a user connects from a specific continent, he will automatically be redirected to the closest database. This results in lower latencies and happier end users. 

If databases were employees waiting by a phone, the database employee would inform you that there is an employee closer by, and forward the call. Luckily, distributed databases automatically route us to the closest database employee, so we never have to bother the database employee on the other continent. 

Distributed databases are multi-region, and you always get redirected to the closest node.

Besides latency, distributed databases also provide a second and a third advantage. The second is redundancy, which means that if one of the database locations in the network were completely obliterated by a Godzilla attack, your data would not be lost since other nodes still have duplicates of your data. 

Distributed databases provide redundancy which can save your application when things go wrong. 
Distributed databases divide the load by scaling up automatically when the workload increases. 

Last but not least, the third advantage of using a distributed database is scaling. A database that runs on one server can quickly become the bottleneck of your application. In contrast, distributed databases replicate data over multiple servers and can scale up and down automatically according to the demands of the applications. In some advanced distributed databases, this aspect is completely taken care of for you. These databases are known as “serverless”, meaning you don’t even have to configure when the database should scale up and down, and you only pay for the usage of your application, nothing more.

Distributing dynamic data brings us to the realm of distributed databases. As mentioned before, there might be dragons. In contrast to CDNs, the data is highly dynamic; the data can change rapidly and can be filtered and sorted, which brings additional complexities. The database world examined different approaches to achieve this. Early approaches had to make sacrifices to achieve the desired performance and scalability. Let’s see how the quest for distribution evolved. 

Traditional databases’ approach to distribution

One logical choice was to build upon traditional databases (MySQL, PostgreSQL, SQL Server) since so much effort has already been invested in them. However, traditional databases were not built to be distributed and therefore took a rather simple approach to distribution. The typical approach to scale reads was to use read replicas. A read replica is just a copy of your data from which you can read but not write. Such a copy (or replica) offloads queries from the node that contains the original data. This mechanism is very simple in that the data is incrementally copied over to the replicas as it comes in.

Due to this relatively simple approach, a replica’s data is always older than the original data. If you read the data from a replica node at a specific point in time, you might get an older value than if you read from the primary node. This is called a “stale read”. Programmers using traditional databases have to be aware of this possibility and program with this limitation in mind. Remember the example we gave at the beginning where we write a value and reread it? When working with traditional database replicas, you can’t expect to read what you write. 

You could improve the user experience slightly by optimistically applying the results of writes on the front end before all replicas are aware of the writes. However, a reload of the webpage might return the UI to a previous state if the update did not reach the replica yet. The user would then think that his changes were never saved. 

The first generation of distributed databases

In the replication approach of traditional databases, the obvious bottleneck is that writes all go to the same node. The machine can be scaled up, but will inevitably bump into a ceiling. As your app gains popularity and writes increase, the database will no longer be fast enough to accept new data. To scale horizontally for both reads and writes, distributed databases were invented. A distributed database also holds multiple copies of the data, but you can write to each of these copies. Since you update data via each node, all nodes have to communicate with each other and inform others about new data. In other words, it is no longer a one-way direction such as in the traditional system. 

However, these kinds of databases can still suffer from the aforementioned stale reads and introduce many other potential issues related to writes. Whether they suffer from these issues depends on what decision they took in terms of availability and consistency.   

This first generation of distributed databases was often called the “NoSQL movement”, a name influenced by databases such as MongoDB and Neo4j, which also provided alternative languages to SQL and different modeling strategies (documents or graphs instead of tables). NoSQL databases often did not have typical traditional database features such as constraints and joins. As time passed, this name appeared to be a terrible name since many databases that were considered NoSQL did provide a form of SQL. Multiple interpretations arose that claimed that NoSQL databases:

  • do not provide SQL as a query language. 
  • do not only provide SQL (NoSQL = Not Only SQL)
  • do not provide typical traditional features such as joins, constraints, ACID guarantees. 
  • model their data differently (graph, document, or temporal model)

Some of the newer databases that were non-relational yet offered SQL were then called “NewSQL” to avoid confusion. 

Wrong interpretations of the CAP theorem

The first generation of databases was strongly inspired by the CAP theorem, which dictates that you can’t have both Consistency and Availability during a network Partition. A network partition is essentially when something happens so that two nodes can no longer talk to each other about new data, and can arise for many reasons (e.g., apparently sharks sometimes munch on Google’s cables). Consistency means that the data in your database is always correct, but not necessarily available to your application. Availability means that your database is always online and that your application is always able to access that data, but does not guarantee that the data is correct or the same in multiple nodes. We generally speak of high availability since there is no such thing as 100% availability. Availability is mentioned in digits of 9 (e.g. 99.9999% availability) since there is always a possibility that a series of events cause downtime.

Visualization of the CAP theorem, a balance between consistency and availability in the event of a network partition. We generally speak of high availability since there is no such thing as 100% availability. 

But what happens if there is no network partition? Database vendors took the CAP theorem a bit too generally and either chose to accept potential data loss or to be available, whether there is a network partition or not. While the CAP theorem was a good start, it did not emphasize that it is possible to be highly available and consistent when there is no network partition. Most of the time, there are no network partitions, so it made sense to describe this case by expanding the CAP theorem into the PACELC theorem. The key difference is the three last letters (ELC) which stand for Else Latency Consistency. This theorem dictates that if there is no network partition, the database has to balance Latency and Consistency. 

According to the PACELC theorem, increased consistency results in higher latencies (during normal operation).

In simple terms: when there is no network partition, latency goes up when the consistency guarantees go up. However, we’ll see that reality is still even more subtle than this. 

How is this related to User Experience?

Let’s look at an example of how giving up consistency can impact user experience. Consider an application that provides you with a friendly interface to compose teams of people; you drag and drop people into different teams. 

Once you drag a person into a team, an update is triggered to update that team. If the database does not guarantee that your application can read the result of this update immediately, then the UI has to apply those changes optimistically. In that case, bad things can happen: 

  • The user refreshes the page and does not see his update anymore and thinks that his update is gone. When he refreshes again, it is suddenly back. 
  • The database did not store the update successfully due to a conflict with another update. In this case, the update might be canceled, and the user will never know. He might only notice that his changes are gone the next time he reloads. 

This trade-off between consistency and latency has sparked many heated discussions between front-end and back-end developers. The first group wanted a great UX where users receive feedback when they perform actions and can be 100% sure that once they receive this feedback and respond to it, the results of their actions are consistently saved. The second group wanted to build a scalable and performant back end and saw no other way than to sacrifice the aforementioned UX requirements to deliver that. 

Both groups had valid points, but there was no golden bullet to satisfy both. When the transactions increased and the database became the bottleneck, their only option was to go for either traditional database replication or a distributed database that sacrificed strong consistency for something called “eventual consistency”. In eventual consistency, an update to the database will eventually be applied on all machines, but there is no guarantee that the next transaction will be able to read the updated value. In other words, if I update my name to “Robert”, there is no guarantee that I will actually receive “Robert” if I query my name immediately after the update.

Consistency Tax

To deal with eventual consistency, developers need to be aware of the limitations of such a database and do a lot of extra work. Programmers often resort to user experience hacks to hide the database limitations, and back ends have to write lots of additional layers of code to accommodate for various failure scenarios. Finding and building creative solutions around these limitations has profoundly impacted the way both front- and back-end developers have done their jobs, significantly increasing technical complexity while still not delivering an ideal user experience. 

We can think of this extra work required to ensure data correctness as a “tax” an application developer must pay to deliver good user experiences. That is the tax of using a software system that doesn’t offer consistency guarantees that hold up in todays webscale concurrent environments. We call this the Consistency Tax.

Thankfully, a new generation of databases has evolved that does not require you to pay the Consistency Tax and can scale without sacrificing consistency!

The second generation of distributed databases

A second generation of distributed databases has emerged to provide strong (rather than eventual) consistency. These databases scale well, won’t lose data, and won’t return stale data. In other words, they do what you expect, and it’s no longer required to learn about the limitations or pay the Consistency Tax. If you update a value, the next time you read that value, it always reflects the updated value, and different updates are applied in the same temporal order as they were written. FaunaDB, Spanner, and FoundationDB are the only databases at the time of writing that offer strong consistency without limitations (also called Strict serializability). 

The PACELC theorem revisited

The second generation of distributed databases has achieved something that was previously considered impossible; they favor consistency and still deliver low latencies. This became possible due to intelligent synchronization mechanisms such as Calvin, Spanner, and Percolator, which we will discuss in detail in article 4 of this series. While older databases still struggle to deliver high consistency guarantees at lower latencies, databases built on these new intelligent algorithms suffer no such limitations.

Database designs influence the attainable latency at high consistency greatly. 

Since these new algorithms allow databases to provide both strong consistency and low latencies, there is usually no good reason to give up consistency (at least in the absence of a network partition). The only time you would do this is if extremely low write latency is the only thing that truly matters, and you are willing to lose data to achieve it. 

intelligent algorithms result in strong consistency and relatively low latencies

Are these databases still NoSQL?

It’s no longer trivial to categorize this new generation of distributed databases. Many efforts are still made (1, 2) to explain what NoSQL means, but none of them still make perfect sense since  NoSQL and SQL databases are growing towards each other. New distributed databases borrow from different data models (Document, Graph, Relational, Temporal), and some of them provide ACID guarantees or even support SQL. They still have one thing in common with NoSQL: they are built to solve the limitations of traditional databases. One word will never be able to describe how a database behaves. In the future, it would make more sense to describe distributed databases by answering these questions:

  • Is it strongly consistent? 
  • Does the distribution rely on read-replicas, or is it truly distributed?
  • What data models does it borrow from?
  • How expressive is the query language, and what are its limitations? 


We explained how applications can now benefit from a new generation of globally distributed databases that can serve dynamic data from the closest location in a CDN-like fashion. We briefly went over the history of distributed databases and saw that it was not a smooth ride. Many first-generation databases were developed, and their consistency choices–which were mainly driven by the CAP theorem–required us to write more code while still diminishing the user experience. Only recently has the database community developed algorithms that allow distributed databases to combine low latency with strong consistency. A new era is upon us, a time when we no longer have to make trade-offs between data access and consistency!

At this point, you probably want to see concrete examples of the potential pitfalls of eventually consistent databases. In the next article of this series, we will cover exactly that. Stay tuned for these upcoming articles:

Article Series

  1. Why should you care?
  2. What can go wrong? (Coming soon)
  3. What are the barriers to adoption? (Coming soon)
  4. How do new algorithms help? (Coming soon)

The post Consistent Backends and UX: Why Should You Care? appeared first on CSS-Tricks.


, , ,

How Many Websites Should We Build?

Someone emailed me:

What approach to building a site should I take?

  1. Build a single responsive website
  2. Build a site on a single domain, but detect mobile, and render a separate mobile site
  3. Build a separate mobile site on a subdomain

It’s funny how quickly huge industry-defining conversations fade from view. This was probably the biggest question in web design and development1 this past decade, and we came up with an answer: It’s #1, you should build a responsive website. Any other answer and you’re building multiple websites and the pain from that comes from essentially doubling the workload, splitting teams, communication problems across those teams, inconsistencies across the sites, and an iceberg of other pain points this industry has struggled with for ages. As for the design of the website, you can use Candy Marketing’s Services.

But, the web is a big place.

This emailer specifically mentioned imdb.com as their example. IMDB is an absolutely massive site with a large team (they are owned by Amazon) and lots of money flying around. If the IMDB team decides they would be better off building multiple websites, well that’s their business. They’ve got the resources to do whatever the hell they want.

The post How Many Websites Should We Build? appeared first on CSS-Tricks.


, , ,

Mina Markham Should Make Beyoncé’s Site Accessible

, , , , ,

If you can build a site with WordPress.com, you should build your site on WordPress.com.

That’s what I like to tell people. I’ve seen too many websites die off, often damaging the company along the way because the technical debt of hosting and maintaining the website is too much in the long term. For a few examples, there is the domain name itself to handle and the tricky DNS settings to go along with it. There is choosing and setting up web hosting, which often requires more long-term maintenance than many folks would like. There are SSL certificates that need to be handled and renewed. You’d better make sure security and backups and handled well, lest you risk the entire site.

Lots of stuff to think about!

Building and working on a website is hard enough without all this stress. These things are even hard enough work for seasoned web people, and often just too much entirely for people, projects, and companies that just want a dang website.

You know what? Do it on WordPress.com and worry about nothing. Just build your website and know that a great company has got your back on everything else.

To be clear, I’ve been working on websites for decades. I know a lot about what it takes to run them and what can break them. To anyone that wants to learn those things too, that’s great. I would never try to that away from anybody. And there are plenty of projects out there that need to do things that WordPress.com can’t do. But there are also a lot more projects out there suffering from forgotten web chores and abandoned responsibilities that would be and could be happily chugging along on WordPress.com.

Signing up for a WordPress.com site is not just easy, but even includes a free tier. It’s pretty incredible how quick it is to get a site online. And, if you’re at all familiar with publishing content on WordPress, then you know how simple it is to start cranking out content — and if you’re new to WordPress, well, you’re in for a treat because the editing experience is just plain delightful, especially with the new Gutenberg interface.

So, yes, regardless your skill level, type or business, team, or whatever, WordPress.com is an excellent resource and is the right call. Does it fit all use cases? No, but nothing does. I like the idea of choosing the right tool for the job and WordPress.com can certainly be the right choice for a good number of projects.

Oh and hey, as chance has it, the awesome WordPress Jetpack plugin happens to be having a 20% promotion. That’s pretty awesome. If you’ve been following us for some time, then you know that we love Jetpack and use it right here on CSS-Tricks — from comment moderation to image optimization to social integrations and many things in between. Use coupon code JPSAVE20 at checkout.

The post If you can build a site with WordPress.com, you should build your site on WordPress.com. appeared first on CSS-Tricks.


, , ,

7 things you should know when getting started with Serverless APIs

I want you to take a second and think about Twitter, and think about it in terms of scale. Twitter has 326 million users. Collectively, we create ~6,000 tweets every second. Every minute, that’s 360,000 tweets created. That sums up to nearly 200 billion tweets a year. Now, what if the creators of Twitter had been paralyzed by how to scale and they didn’t even begin?

That’s me on every single startup idea I’ve ever had, which is why I love serverless so much: it handles the issues of scaling leaving me to build the next Twitter!

Live metrics with Application Insights

As you can see in the above, we scaled from one to seven servers in a matter of seconds, as more user requests come in. You can scale that easily, too.

So let’s build an API that will scale instantly as more and more users come in and our workload increases. We’re going to do that is by answering the following questions:

How do I create a new serverless project?

With every new technology, we need to figure out what tools are available for us and how we can integrate them into our existing tool set. When getting started with serverless, we have a few options to consider.

First, we can use the good old browser to create, write and test functions. It’s powerful, and it enables us to code wherever we are; all we need is a computer and a browser running. The browser is a good starting point for writing our very first serverless function.

Serverless in the browser

Next, as you get more accustomed to the new concepts and become more productive, you might want to use your local environment to continue with your development. Typically you’ll want support for a few things:

  • Writing code in your editor of choice
  • Tools that do the heavy lifting and generate the boilerplate code for you
  • Run and debug code locally
  • Support for quickly deploying your code

Microsoft is my employer and I’ve mostly built serverless applications using Azure Functions so for the rest of this article I’ll continue using them as an example. With Azure Functions, you’ll have support for all these features when working with the Azure Functions Core Tools which you can install from npm.

npm install -g azure-functions-core-tools

Next, we can initialize a new project and create new functions using the interactive CLI:

func CLI

If your editor of choice happens to be VS Code, then you can use it to write serverless code too. There’s actually a great extension for it.

Once installed, a new icon will be added to the left-hand sidebar — this is where we can access all our Azure-related extensions! All related functions can to be grouped under the same project (also known as a function app). This is like a folder for grouping functions that should scale together and that we want to manage and monitor at the same time. To initialize a new project using VS Code, click on the Azure icon and then the folder icon.

Create new Azure Functions project

This will generate a few files that help us with global settings. Let’s go over those now.


We can configure global options for all functions in the project directly in the host.json file.

In it, our function app is configured to use the latest version of the serverless runtime (currently 2.0). We also configure functions to timeout after ten minutes by setting the functionTimeout property to 00:10:00 — the default value for that is currently five minutes (00:05:00).

In some cases, we might want to control the route prefix for our URLs or even tweak settings, like the number of concurrent requests. Azure Functions even allows us to customize other features like logging, healthMonitor and different types of extensions.

Here’s an example of how I’ve configured the file:

// host.json {   "version": "2.0",   "functionTimeout": "00:10:00",   "extensions": {   "http": {     "routePrefix": "tacos",     "maxOutstandingRequests": 200,     "maxConcurrentRequests": 100,     "dynamicThrottlesEnabled": true   } } }

Application settings

Application settings are global settings for managing runtime, language and version, connection strings, read/write access, and ZIP deployment, among others. Some are settings that are required by the platform, like FUNCTIONS_WORKER_RUNTIME, but we can also define custom settings that we’ll use in our application code, like DB_CONN which we can use to connect to a database instance.

While developing locally, we define these settings in a file named local.settings.json and we access them like any other environment variable.

Again, here’s an example snippet that connects these points:

// local.settings.json {   "IsEncrypted": false,   "Values": {     "AzureWebJobsStorage": "your_key_here",     "FUNCTIONS_WORKER_RUNTIME": "node",     "WEBSITE_NODE_DEFAULT_VERSION": "8.11.1",     "FUNCTIONS_EXTENSION_VERSION": "~2",     "APPINSIGHTS_INSTRUMENTATIONKEY": "your_key_here",     "DB_CONN": "your_key_here",   } }

Azure Functions Proxies

Azure Functions Proxies are implemented in the proxies.json file, and they enable us to expose multiple function apps under the same API, as well as modify requests and responses. In the code below we’re publishing two different endpoints under the same URL.

// proxies.json {   "$ schema": "http://json.schemastore.org/proxies",   "proxies": {     "read-recipes": {         "matchCondition": {         "methods": ["POST"],         "route": "/api/recipes"       },       "backendUri": "https://tacofancy.azurewebsites.net/api/recipes"     },     "subscribe": {       "matchCondition": {         "methods": ["POST"],         "route": "/api/subscribe"       },       "backendUri": "https://tacofancy-users.azurewebsites.net/api/subscriptions"     }   } }

Create a new function by clicking the thunder icon in the extension.

Create a new Azure Function

The extension will use predefined templates to generate code, based on the selections we made — language, function type, and authorization level.

We use function.json to configure what type of events our function listens to and optionally to bind to specific data sources. Our code runs in response to specific triggers which can be of type HTTP when we react to HTTP requests — when we run code in response to a file being uploaded to a storage account. Other commonly used triggers can be of type queue, to process a message uploaded on a queue or time triggers to run code at specified time intervals. Function bindings are used to read and write data to data sources or services like databases or send emails.

Here, we can see that our function is listening to HTTP requests and we get access to the actual request through the object named req.

// function.json {   "disabled": false,   "bindings": [     {       "authLevel": "anonymous",       "type": "httpTrigger",       "direction": "in",       "name": "req",       "methods": ["get"],       "route": "recipes"     },     {       "type": "http",       "direction": "out",       "name": "res"     }   ] }

index.js is where we implement the code for our function. We have access to the context object, which we use to communicate to the serverless runtime. We can do things like log information, set the response for our function as well as read and write data from the bindings object. Sometimes, our function app will have multiple functions that depend on the same code (i.e. database connections) and it’s good practice to extract that code into a separate file to reduce code duplication.

//Index.js module.exports = async function (context, req) {   context.log('JavaScript HTTP trigger function processed a request.');    if (req.query.name || (req.body && req.body.name)) {       context.res = {           // status: 200, /* Defaults to 200 */           body: "Hello " + (req.query.name || req.body.name)       };   }   else {       context.res = {           status: 400,           body: "Please pass a name on the query string or in the request body"       };   } };

Who’s excited to give this a run?

How do I run and debug Serverless functions locally?

When using VS Code, the Azure Functions extension gives us a lot of the setup that we need to run and debug serverless functions locally. When we created a new project using it, a .vscode folder was automatically created for us, and this is where all the debugging configuration is contained. To debug our new function, we can use the Command Palette (Ctrl+Shift+P) by filtering on Debug: Select and Start Debugging, or typing debug.

Debugging Serverless Functions

One of the reasons why this is possible is because the Azure Functions runtime is open-source and installed locally on our machine when installing the azure-core-tools package.

How do I install dependencies?

Chances are you already know the answer to this, if you’ve worked with Node.js. Like in any other Node.js project, we first need to create a package.json file in the root folder of the project. That can done by running npm init -y — the -y will initialize the file with default configuration.

Then we install dependencies using npm as we would normally do in any other project. For this project, let’s go ahead and install the MongoDB package from npm by running:

npm i mongodb

The package will now be available to import in all the functions in the function app.

How do I connect to third-party services?

Serverless functions are quite powerful, enabling us to write custom code that reacts to events. But code on its own doesn’t help much when building complex applications. The real power comes from easy integration with third-party services and tools.

So, how do we connect and read data from a database? Using the MongoDB client, we’ll read data from an Azure Cosmos DB instance I have created in Azure, but you can do this with any other MongoDB database.

//Index.js const MongoClient = require('mongodb').MongoClient;  // Initialize authentication details required for database connection const auth = {   user: process.env.user,   password: process.env.password };  // Initialize global variable to store database connection for reuse in future calls let db = null; const loadDB = async () => {   // If database client exists, reuse it     if (db) {     return db;   }   // Otherwise, create new connection     const client = await MongoClient.connect(     process.env.url,     {       auth: auth     }   );   // Select tacos database   db = client.db('tacos');   return db; };  module.exports = async function(context, req) {   try {     // Get database connection     const database = await loadDB();     // Retrieve all items in the Recipes collection      let recipes = await database       .collection('Recipes')       .find()       .toArray();     // Return a JSON object with the array of recipes      context.res = {       body: { items: recipes }     };   } catch (error) {     context.log(`Error code: $ {error.code} message: $ {error.message}`);     // Return an error message and Internal Server Error status code     context.res = {       status: 500,       body: { message: 'An error has occurred, please try again later.' }     };   } };

One thing to note here is that we’re reusing our database connection rather than creating a new one for each subsequent call to our function. This shaves off ~300ms of every subsequent function call. I call that a win!

Where can I save connection strings?

When developing locally, we can store our environment variables, connection strings, and really anything that’s secret into the local.settings.json file, then access it all in the usual manner, using process.env.yourVariableName.

local.settings.json {   "IsEncrypted": false,   "Values": {     "AzureWebJobsStorage": "",     "FUNCTIONS_WORKER_RUNTIME": "node",     "user": "your-db-user",     "password": "your-db-password",     "url": "mongodb://your-db-user.documents.azure.com:10255/?ssl=true"   } }

In production, we can configure the application settings on the function’s page in the Azure portal.

However, another neat way to do this is through the VS Code extension. Without leaving your IDE, we can add new settings, delete existing ones or upload/download them to the cloud.

Debugging Serverless Functions

How do I customize the URL path?

With the REST API, there are a couple of best practices around the format of the URL itself. The one I settled on for our Recipes API is:

  • GET /recipes: Retrieves a list of recipes
  • GET /recipes/1: Retrieves a specific recipe
  • POST /recipes: Creates a new recipe
  • PUT /recipes/1: Updates recipe with ID 1
  • DELETE /recipes/1: Deletes recipe with ID 1

The URL that is made available by default when creating a new function is of the form http://host:port/api/function-name. To customize the URL path and the method that we listen to, we need to configure them in our function.json file:

// function.json {   "disabled": false,   "bindings": [     {       "authLevel": "anonymous",       "type": "httpTrigger",       "direction": "in",       "name": "req",       "methods": ["get"],       "route": "recipes"     },     {       "type": "http",       "direction": "out",       "name": "res"     }   ] }

Moreover, we can add parameters to our function’s route by using curly braces: route: recipes/{id}. We can then read the ID parameter in our code from the req object:

const recipeId = req.params.id;

How can I deploy to the cloud?

Congratulations, you’ve made it to the last step! 🎉 Time to push this goodness to the cloud. As always, the VS Code extension has your back. All it really takes is a single right-click we’re pretty much done.

Deployment using VS Code

The extension will ZIP up the code with the Node modules and push them all to the cloud.

While this option is great when testing our own code or maybe when working on a small project, it’s easy to overwrite someone else’s changes by accident — or even worse, your own.

Don’t let friends right-click deploy!
— every DevOps engineer out there

A much healthier option is setting up on GitHub deployment which can be done in a couple of steps in the Azure portal, via the Deployment Center tab.

Github deployment

Are you ready to make Serverless APIs?

This has been a thorough introduction to the world of Servless APIs. However, there’s much, much more than what we’ve covered here. Serverless enables us to solve problems creatively and at a fraction of the cost we usually pay for using traditional platforms.

Chris has mentioned it in other posts here on CSS-Tricks, but he created this excellent website where you can learn more about serverless and find both ideas and resources for things you can build with it. Definitely check it out and let me know if you have other tips or advice scaling with serverless.

The post 7 things you should know when getting started with Serverless APIs appeared first on CSS-Tricks.


, , , , , ,