CINXE.COM

<!DOCTYPE html><html lang="en"><head><meta charSet="utf-8" data-next-head=""/><meta name="viewport" content="width=device-width" data-next-head=""/><meta name="twitter:card" content="summary_large_image" data-next-head=""/><meta name="twitter:site" content="@appsignal" data-next-head=""/><meta name="twitter:creator" content="@appsignal" data-next-head=""/><meta content="AppSignal" name="publisher" data-next-head=""/><meta content="2021 AppSignal" name="copyright" data-next-head=""/><meta content="https://blog.appsignal.com" name="host" data-next-head=""/><meta content="Middleman App" name="generator" data-next-head=""/><title data-next-head="">How to Use gRPC in Elixir | AppSignal Blog</title><meta name="robots" content="index,follow,max-image-preview:large" data-next-head=""/><meta name="description" content="Learn about gRPC and how to use it in an Elixir application." data-next-head=""/><meta property="og:title" content="How to Use gRPC in Elixir | AppSignal Blog" data-next-head=""/><meta property="og:description" content="Learn about gRPC and how to use it in an Elixir application." data-next-head=""/><meta property="og:url" content="https://blog.appsignal.com/2020/03/24/how-to-use-grpc-in-elixir.html" data-next-head=""/><meta property="og:type" content="article" data-next-head=""/><meta property="og:image" content="https://ondemand.bannerbear.com/signedurl/vYR1M6Lyq22EAnXbgZ/image.jpg?modifications=W3sibmFtZSI6InRpdGxlIiwidGV4dCI6IkhvdyB0byBVc2UgZ1JQQyBpbiBFbGl4aXIifSx7Im5hbWUiOiJpbWFnZSIsImltYWdlX3VybCI6Imh0dHBzOi8vYXBwc2lnbmFsLW5leHRqcy1ibG9nLTdoYzlub2p5ZC1hcHBzaWduYWwudmVyY2VsLmFwcC9pbWFnZXMvYmxvZy8yMDIwLTAzL2hvdy10by11c2UtZ3JwYy1pbi1lbGl4aXIuanBnIn0seyJuYW1lIjoiY2F0ZWdvcnlfbG9nbyIsImltYWdlX3VybCI6Imh0dHBzOi8vYXBwc2lnbmFsLW5leHRqcy1ibG9nLTdoYzlub2p5ZC1hcHBzaWduYWwudmVyY2VsLmFwcC9pbWFnZXMvbG9nb3MvZWxpeGlyLWxvZ28ucG5nIn1d&amp;s=35074699d9c0ceffe6529df8a7aae24219244bc27297a424661ef05cd24efed3" data-next-head=""/><meta property="og:image:alt" content="How to Use gRPC in Elixir" data-next-head=""/><meta property="og:image:type" content="image/jpeg" data-next-head=""/><meta property="og:image:width" content="1200" data-next-head=""/><meta property="og:image:height" content="628" data-next-head=""/><link rel="canonical" href="https://blog.appsignal.com/2020/03/24/how-to-use-grpc-in-elixir.html" data-next-head=""/><meta name="author" content="Alex Koutmos" data-next-head=""/><meta name="article:published_time" content="2020-03-24T00:00:00+00:00" data-next-head=""/><link rel="alternate" type="application/rss+xml" title="AppSignal" href="https://blog.appsignal.com/category/elixir-feed.xml" data-next-head=""/><link rel="icon" href="/favicon/favicon.svg" type="image/svg+xml"/><link rel="mask-icon" href="/favicon/favicon.svg" color="#29A575"/><link rel="apple-touch-icon" href="/favicon/apple-touch-icon.png"/><link rel="manifest" href="/favicon/manifest.webmanifest"/><link rel="preload" href="/fonts/rubik-v12-latin-regular.woff2" as="font" type="font/woff2" crossorigin="anonymous"/><link rel="preload" href="/fonts/rubik-v12-latin-500.woff2" as="font" type="font/woff2" crossorigin="anonymous"/><meta name="ahrefs-site-verification" content="eaeabe5fcccf5783b1b0c574c52a3306af55ea1ab27e5eed6405393ed63feae6"/><link rel="preload" href="/_next/static/css/a392a56cf9246d26.css" as="style"/><script type="application/ld+json" data-next-head="">{"@context":"https://schema.org","@type":"BlogPosting","datePublished":"2020-03-24T00:00:00+00:00","description":"Learn about gRPC and how to use it in an Elixir application.","mainEntityOfPage":{"@type":"WebPage","@id":"https://blog.appsignal.com/2020/03/24/how-to-use-grpc-in-elixir.html"},"headline":"How to Use gRPC in Elixir","image":["https://ondemand.bannerbear.com/signedurl/vYR1M6Lyq22EAnXbgZ/image.jpg?modifications=W3sibmFtZSI6InRpdGxlIiwidGV4dCI6IkhvdyB0byBVc2UgZ1JQQyBpbiBFbGl4aXIifSx7Im5hbWUiOiJpbWFnZSIsImltYWdlX3VybCI6Imh0dHBzOi8vYXBwc2lnbmFsLW5leHRqcy1ibG9nLTdoYzlub2p5ZC1hcHBzaWduYWwudmVyY2VsLmFwcC9pbWFnZXMvYmxvZy8yMDIwLTAzL2hvdy10by11c2UtZ3JwYy1pbi1lbGl4aXIuanBnIn0seyJuYW1lIjoiY2F0ZWdvcnlfbG9nbyIsImltYWdlX3VybCI6Imh0dHBzOi8vYXBwc2lnbmFsLW5leHRqcy1ibG9nLTdoYzlub2p5ZC1hcHBzaWduYWwudmVyY2VsLmFwcC9pbWFnZXMvbG9nb3MvZWxpeGlyLWxvZ28ucG5nIn1d&amp;amp;s=35074699d9c0ceffe6529df8a7aae24219244bc27297a424661ef05cd24efed3"],"dateModified":"2020-03-24T00:00:00+00:00","author":[{"@type":"Person","name":"Alex Koutmos","url":"https://blog.appsignal.com/authors/alex-koutmos.html"}],"publisher":{"@type":"Organization","name":"AppSignal","logo":{"@type":"ImageObject","url":"https://appsignal.com/images/logo-appsignal.svg"}}}</script><link rel="preload" as="image" imageSrcSet="/_next/image?url=%2Fimages%2Fblog%2F2020-03%2Fhow-to-use-grpc-in-elixir.jpg&amp;w=640&amp;q=50 640w, /_next/image?url=%2Fimages%2Fblog%2F2020-03%2Fhow-to-use-grpc-in-elixir.jpg&amp;w=750&amp;q=50 750w, /_next/image?url=%2Fimages%2Fblog%2F2020-03%2Fhow-to-use-grpc-in-elixir.jpg&amp;w=828&amp;q=50 828w, /_next/image?url=%2Fimages%2Fblog%2F2020-03%2Fhow-to-use-grpc-in-elixir.jpg&amp;w=1080&amp;q=50 1080w, /_next/image?url=%2Fimages%2Fblog%2F2020-03%2Fhow-to-use-grpc-in-elixir.jpg&amp;w=1200&amp;q=50 1200w, /_next/image?url=%2Fimages%2Fblog%2F2020-03%2Fhow-to-use-grpc-in-elixir.jpg&amp;w=1920&amp;q=50 1920w, /_next/image?url=%2Fimages%2Fblog%2F2020-03%2Fhow-to-use-grpc-in-elixir.jpg&amp;w=2048&amp;q=50 2048w, /_next/image?url=%2Fimages%2Fblog%2F2020-03%2Fhow-to-use-grpc-in-elixir.jpg&amp;w=3840&amp;q=50 3840w" imageSizes="(min-width: 1024px) 1200px, 100vw" data-next-head=""/><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.13.11/dist/katex.min.css" integrity="sha384-Um5gpz1odJg5Z4HAmzPtgZKdTBHZdw8S29IecapCSB31ligYPhHQZMIlWLYQGVoc" crossorigin="anonymous"/><link rel="stylesheet" href="/_next/static/css/a392a56cf9246d26.css" data-n-g=""/><noscript data-n-css=""></noscript><script defer="" noModule="" src="/_next/static/chunks/polyfills-42372ed130431b0a.js"></script><script defer="" src="/_next/static/chunks/142.2a93f122278f6e7b.js"></script><script defer="" src="/_next/static/chunks/572.5947568499de7fc9.js"></script><script defer="" src="/_next/static/chunks/653.32a136c287cb6a47.js"></script><script defer="" src="/_next/static/chunks/396.b5e21ccc37006670.js"></script><script src="/_next/static/chunks/webpack-8c6213fdb13c68bb.js" defer=""></script><script src="/_next/static/chunks/framework-09d0adfeb2fe406a.js" defer=""></script><script src="/_next/static/chunks/main-b20d2c8c60474cbf.js" defer=""></script><script src="/_next/static/chunks/pages/_app-3d303ccdab66c074.js" defer=""></script><script src="/_next/static/chunks/965-2551e136cd282557.js" defer=""></script><script src="/_next/static/chunks/132-0b54ed7d2039c49b.js" defer=""></script><script src="/_next/static/chunks/200-b6168dc33fe3a5f2.js" defer=""></script><script src="/_next/static/chunks/pages/%5Byear%5D/%5Bmonth%5D/%5Bday%5D/%5Btext%5D-cbc22e28112b1907.js" defer=""></script><script src="/_next/static/4VD4GTi1vs_NVN2Wl-Jyb/_buildManifest.js" defer=""></script><script src="/_next/static/4VD4GTi1vs_NVN2Wl-Jyb/_ssgManifest.js" defer=""></script></head><body class="font-rubik"><link rel="preload" as="image" imageSrcSet="/_next/image?url=%2Fimages%2Fblog%2F2020-03%2Fhow-to-use-grpc-in-elixir.jpg&amp;w=640&amp;q=50 640w, /_next/image?url=%2Fimages%2Fblog%2F2020-03%2Fhow-to-use-grpc-in-elixir.jpg&amp;w=750&amp;q=50 750w, /_next/image?url=%2Fimages%2Fblog%2F2020-03%2Fhow-to-use-grpc-in-elixir.jpg&amp;w=828&amp;q=50 828w, /_next/image?url=%2Fimages%2Fblog%2F2020-03%2Fhow-to-use-grpc-in-elixir.jpg&amp;w=1080&amp;q=50 1080w, /_next/image?url=%2Fimages%2Fblog%2F2020-03%2Fhow-to-use-grpc-in-elixir.jpg&amp;w=1200&amp;q=50 1200w, /_next/image?url=%2Fimages%2Fblog%2F2020-03%2Fhow-to-use-grpc-in-elixir.jpg&amp;w=1920&amp;q=50 1920w, /_next/image?url=%2Fimages%2Fblog%2F2020-03%2Fhow-to-use-grpc-in-elixir.jpg&amp;w=2048&amp;q=50 2048w, /_next/image?url=%2Fimages%2Fblog%2F2020-03%2Fhow-to-use-grpc-in-elixir.jpg&amp;w=3840&amp;q=50 3840w" imageSizes="(min-width: 1024px) 1200px, 100vw"/><div id="__next"><!--$--><!--/$--><header class="sticky z-40 top-0 w-full xl:overflow-y-visible bg-gray-900 text-white border-b border-b-gray-800 border-b border-b-gray-800"><div class="c-container flex justify-between items-center py-4 false"><a href="https://www.appsignal.com" class="flex"><div class="w-[130px] h-[24px]"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 350.92 64.54"><g fill="#fff"><path d="M341.56 12.58c-5.46 0-9.37 4.82-13.39 10.18C325.68 11.38 322.9 0 314.07 0S302.46 11.38 300 22.76c-4-5.36-7.93-10.18-13.39-10.18-3.47 0-9.35 2.37-9.35 11.22v16.11c0 8.69 4.64 10.82 7.42 11.54 4.8 1.25 16.36 2.4 29.37 2.4s24.57-1.15 29.36-2.4c2.79-.72 7.43-2.85 7.43-11.54V23.8c0-8.85-5.88-11.22-9.36-11.22M314.07 5c5.29 0 7.3 10.44 10.12 23-3.09 3.79-6.36 6.86-10.12 6.86S307 31.78 304 28c2.63-11.72 4.65-23 10.12-23m-31.84 34.91V23.8c0-5.59 3-6.2 4.33-6.2 3.85 0 8 6.43 11.94 11.59-2.83 11.71-5.78 18.81-12.75 17.37-1.82-.5-3.52-1.65-3.52-6.65M296 48.05c3-3.35 5-8.58 6.5-14.12 3.23 3.38 6.95 5.93 11.57 5.93s8.33-2.55 11.56-5.93c1.5 5.54 3.47 10.77 6.55 14.12-5.39.5-11.88.79-18.11.79s-12.73-.29-18.12-.79m49.91-8.14c0 5-1.71 6.15-3.52 6.65-7 1.48-9.93-5.66-12.72-17.37 3.95-5.16 8.09-11.59 11.94-11.59 1.3 0 4.34.61 4.34 6.2ZM1.13 52.07a1.12 1.12 0 0 1-.79-.34 1.16 1.16 0 0 1-.34-.85 1.52 1.52 0 0 1 .11-.52L14 13.74a2 2 0 0 1 .62-1 1.81 1.81 0 0 1 1.25-.4h5a1.81 1.81 0 0 1 1.25.4 2.44 2.44 0 0 1 .68 1l13.82 36.62a2.52 2.52 0 0 1 .06.52 1.16 1.16 0 0 1-.34.85 1.12 1.12 0 0 1-.79.34h-3.92a1.4 1.4 0 0 1-1.07-.37 2.41 2.41 0 0 1-.46-.7l-2.89-7.48H9.47L6.63 51a2 2 0 0 1-.48.66 1.5 1.5 0 0 1-1.1.37Zm10.15-14.18H25.4l-7.09-18.82ZM42.52 62.84a1.25 1.25 0 0 1-1.3-1.31V23.89a1.24 1.24 0 0 1 1.3-1.3h3.57a1.25 1.25 0 0 1 1.31 1.3v2.38a11.6 11.6 0 0 1 3.62-3A11.35 11.35 0 0 1 56.58 22a12.67 12.67 0 0 1 5.33 1 10.13 10.13 0 0 1 3.68 2.83 12.56 12.56 0 0 1 2.24 4.26 19.79 19.79 0 0 1 .88 5.21c0 .64.06 1.31.06 2s0 1.34-.06 2a18.1 18.1 0 0 1-.82 5.16 13.14 13.14 0 0 1-2.24 4.22 10.21 10.21 0 0 1-3.71 2.89 12.56 12.56 0 0 1-5.36 1.05 11.36 11.36 0 0 1-5.36-1.16 10.55 10.55 0 0 1-3.54-3v13a1.27 1.27 0 0 1-.37.94 1.38 1.38 0 0 1-1 .37ZM55 47.3a6.63 6.63 0 0 0 4.17-1.16 6.53 6.53 0 0 0 2.18-3 14.43 14.43 0 0 0 .8-4.14 25.8 25.8 0 0 0 0-3.29 14.43 14.43 0 0 0-.8-4.14 6.58 6.58 0 0 0-2.18-3A6.69 6.69 0 0 0 55 27.35a6.44 6.44 0 0 0-6.49 4.22 12.43 12.43 0 0 0-.82 3.77q-.06 1-.06 2.16c0 .79 0 1.53.06 2.21a9.3 9.3 0 0 0 .85 3.6 7.15 7.15 0 0 0 2.38 2.86A6.83 6.83 0 0 0 55 47.3ZM75.71 62.84a1.27 1.27 0 0 1-1.3-1.31V23.89a1.25 1.25 0 0 1 1.3-1.3h3.58a1.29 1.29 0 0 1 .93.36 1.27 1.27 0 0 1 .37.94v2.38a11.53 11.53 0 0 1 3.63-3A11.29 11.29 0 0 1 89.77 22a12.64 12.64 0 0 1 5.33 1 10.16 10.16 0 0 1 3.69 2.83 12.74 12.74 0 0 1 2.21 4.3 20.21 20.21 0 0 1 .88 5.21v4a18.1 18.1 0 0 1-.83 5.16 12.94 12.94 0 0 1-2.24 4.22 10.21 10.21 0 0 1-3.71 2.89 12.53 12.53 0 0 1-5.36 1.05 11.31 11.31 0 0 1-5.35-1.16 10.49 10.49 0 0 1-3.55-3v13a1.27 1.27 0 0 1-.37.94 1.34 1.34 0 0 1-1 .37ZM88.19 47.3a6.1 6.1 0 0 0 6.35-4.19 14.43 14.43 0 0 0 .79-4.11 25.8 25.8 0 0 0 0-3.29 14.43 14.43 0 0 0-.79-4.14 6.1 6.1 0 0 0-6.35-4.19 6.44 6.44 0 0 0-6.5 4.22 12.74 12.74 0 0 0-.82 3.77c0 .64-.05 1.36-.05 2.16s0 1.53.05 2.21a9.3 9.3 0 0 0 .85 3.6 7.15 7.15 0 0 0 2.38 2.86 6.84 6.84 0 0 0 4.09 1.1ZM122.17 52.63a21.18 21.18 0 0 1-8.56-1.53 12.82 12.82 0 0 1-5.27-4 9.44 9.44 0 0 1-1.93-5.25 1.1 1.1 0 0 1 .34-.79 1.15 1.15 0 0 1 .85-.34h4a1.47 1.47 0 0 1 1 .31 2.75 2.75 0 0 1 .57.76 6.36 6.36 0 0 0 1.36 2.39 7.72 7.72 0 0 0 2.86 2 12.06 12.06 0 0 0 4.73.79q4.48 0 6.61-1.53a4.88 4.88 0 0 0 2.12-4.19 3.83 3.83 0 0 0-1.13-2.87 10.56 10.56 0 0 0-3.52-2c-1.58-.6-3.64-1.25-6.17-1.93a36.52 36.52 0 0 1-7-2.55 10.29 10.29 0 0 1-4.19-3.57 9.81 9.81 0 0 1-1.39-5.44 9.72 9.72 0 0 1 1.73-5.67 11.66 11.66 0 0 1 4.93-3.94 18.66 18.66 0 0 1 7.68-1.45 19 19 0 0 1 6.35 1 14 14 0 0 1 4.57 2.55 11.53 11.53 0 0 1 2.77 3.4 8.31 8.31 0 0 1 1 3.57 1.18 1.18 0 0 1-.32.77 1.09 1.09 0 0 1-.87.37h-4.14a1.83 1.83 0 0 1-.88-.23 1.5 1.5 0 0 1-.65-.85 5.22 5.22 0 0 0-2.44-3.46 9.49 9.49 0 0 0-5.39-1.42 10.19 10.19 0 0 0-5.41 1.28 4.35 4.35 0 0 0-2.07 4 4.24 4.24 0 0 0 1 2.89 8.38 8.38 0 0 0 3.18 2 58.53 58.53 0 0 0 5.75 1.9 43.26 43.26 0 0 1 7.76 2.54 10.38 10.38 0 0 1 4.4 3.51 9.64 9.64 0 0 1 1.41 5.42 10 10 0 0 1-1.95 6.2 12.32 12.32 0 0 1-5.44 4 22 22 0 0 1-8.25 1.36ZM143.77 17.37a1.34 1.34 0 0 1-1-.37 1.24 1.24 0 0 1-.37-.93v-3.58a1.34 1.34 0 0 1 .37-1 1.32 1.32 0 0 1 1-.4h4.31a1.39 1.39 0 0 1 1 .4 1.29 1.29 0 0 1 .4 1v3.58a1.2 1.2 0 0 1-.4.93 1.41 1.41 0 0 1-1 .37Zm.34 34.7a1.38 1.38 0 0 1-1-.37 1.27 1.27 0 0 1-.37-.94V23.89a1.27 1.27 0 0 1 .37-.94 1.37 1.37 0 0 1 1-.36h3.69a1.25 1.25 0 0 1 1.3 1.3v26.87a1.27 1.27 0 0 1-.37.94 1.28 1.28 0 0 1-.93.37ZM168.5 64.54a17.5 17.5 0 0 1-6.58-1.05 12.16 12.16 0 0 1-4-2.52 9.27 9.27 0 0 1-2.09-2.97 8.1 8.1 0 0 1-.65-2.38 1.13 1.13 0 0 1 .34-1 1.3 1.3 0 0 1 1-.39h3.86a1.51 1.51 0 0 1 .85.22 1.8 1.8 0 0 1 .56 1 11.19 11.19 0 0 0 1 1.73 5 5 0 0 0 1.93 1.61 7.69 7.69 0 0 0 3.57.68 11 11 0 0 0 4.05-.65 4.82 4.82 0 0 0 2.5-2.3 9.59 9.59 0 0 0 .87-4.47v-3.84a10.93 10.93 0 0 1-3.64 2.79 11.93 11.93 0 0 1-5.38 1.08 12.69 12.69 0 0 1-5.36-1 9.78 9.78 0 0 1-3.69-2.86 13.36 13.36 0 0 1-2.21-4.28 18.82 18.82 0 0 1-.82-5.27q-.06-1.53 0-3.12a18.94 18.94 0 0 1 .8-5.16 12.85 12.85 0 0 1 2.21-4.39 10.34 10.34 0 0 1 3.71-2.94 12.23 12.23 0 0 1 5.36-1.06 11.1 11.1 0 0 1 5.52 1.25 11.77 11.77 0 0 1 3.66 3.06V24a1.37 1.37 0 0 1 .37-1 1.31 1.31 0 0 1 1-.39h3.57a1.33 1.33 0 0 1 1.35 1.39v27.39a16.14 16.14 0 0 1-1.38 6.91 10 10 0 0 1-4.4 4.59 16.32 16.32 0 0 1-7.88 1.65Zm-.17-17.69a6.62 6.62 0 0 0 4.11-1.19 7.39 7.39 0 0 0 2.33-3 10.87 10.87 0 0 0 .87-3.71c0-.49.06-1.12.06-1.9s0-1.39-.06-1.84a11.09 11.09 0 0 0-.87-3.74 7.13 7.13 0 0 0-2.33-3 6.74 6.74 0 0 0-4.11-1.16 6.85 6.85 0 0 0-4.17 1.16 6.49 6.49 0 0 0-2.24 3 14.43 14.43 0 0 0-.79 4.14v2.84a14.62 14.62 0 0 0 .79 4.11 6.52 6.52 0 0 0 2.24 3.06 6.85 6.85 0 0 0 4.17 1.23ZM190.13 52.07a1.38 1.38 0 0 1-1-.37 1.31 1.31 0 0 1-.37-.94V23.89a1.31 1.31 0 0 1 .37-.94 1.37 1.37 0 0 1 1-.36h3.63a1.25 1.25 0 0 1 1.31 1.3v2.44a12.29 12.29 0 0 1 3.8-3.09 11.91 11.91 0 0 1 5.72-1.24 11.34 11.34 0 0 1 6.12 1.59 10.28 10.28 0 0 1 3.92 4.41 15.55 15.55 0 0 1 1.37 6.77v16a1.25 1.25 0 0 1-.4.94 1.39 1.39 0 0 1-1 .37h-3.92a1.28 1.28 0 0 1-.93-.37 1.27 1.27 0 0 1-.37-.94V35.06a8.33 8.33 0 0 0-1.79-5.67 6.45 6.45 0 0 0-5.13-2 6.84 6.84 0 0 0-5.18 2 7.83 7.83 0 0 0-2 5.67v15.7a1.22 1.22 0 0 1-.4.94 1.37 1.37 0 0 1-1 .37ZM230.92 52.63a11.76 11.76 0 0 1-5.25-1.16 9.93 9.93 0 0 1-3.74-3.09 7.31 7.31 0 0 1-1.39-4.38 7.42 7.42 0 0 1 3.18-6.27 18.1 18.1 0 0 1 8.39-3.09l8-1.19v-1.4a5.5 5.5 0 0 0-1.36-4c-.91-.94-2.42-1.41-4.54-1.41a6.9 6.9 0 0 0-3.74.9 5.22 5.22 0 0 0-2 2.44 1.32 1.32 0 0 1-1.25.74h-3.51a1.2 1.2 0 0 1-.94-.34 1.34 1.34 0 0 1-.31-.91 5.88 5.88 0 0 1 .71-2.12 8.28 8.28 0 0 1 2.07-2.5 11.76 11.76 0 0 1 3.66-2 15.47 15.47 0 0 1 5.35-.82 17.06 17.06 0 0 1 5.9.88 9.93 9.93 0 0 1 3.8 2.35 9.17 9.17 0 0 1 2.07 3.4 12.46 12.46 0 0 1 .65 4v18.1a1.22 1.22 0 0 1-.4.94 1.37 1.37 0 0 1-1 .37h-3.63a1.25 1.25 0 0 1-1.3-1.31v-2.38a9.3 9.3 0 0 1-2 2 10.74 10.74 0 0 1-3 1.62 13.85 13.85 0 0 1-4.42.63Zm1.59-4.82a8.24 8.24 0 0 0 3.91-.93A6.74 6.74 0 0 0 239.2 44a10.38 10.38 0 0 0 1-4.88v-1.4l-6.13 1a13.25 13.25 0 0 0-5.52 1.75 3.66 3.66 0 0 0-1.85 3.06 3.46 3.46 0 0 0 .85 2.41 5.33 5.33 0 0 0 2.16 1.42 8 8 0 0 0 2.8.45ZM254.47 52.07a1.25 1.25 0 0 1-1.3-1.31V13.12a1.25 1.25 0 0 1 1.3-1.31h3.74a1.25 1.25 0 0 1 1.3 1.31v37.64a1.25 1.25 0 0 1-1.3 1.31Z"></path></g></svg></div></a><div class="hidden xl:block"><ul class="flex"><li><div class="block py-2 px-2 xl:px-3 no-underline cursor-pointer select-none text-gray-200 hover:text-white"><span>Features</span><svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="angle-down" class="svg-inline--fa fa-angle-down inline-block pb-0.5 ml-2 text-gray-400 text-xxs" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><path fill="currentColor" d="M201.4 374.6c12.5 12.5 32.8 12.5 45.3 0l160-160c12.5-12.5 12.5-32.8 0-45.3s-32.8-12.5-45.3 0L224 306.7 86.6 169.4c-12.5-12.5-32.8-12.5-45.3 0s-12.5 32.8 0 45.3l160 160z"></path></svg><div class="absolute mt-0 pt-2.5 z-10 w-72 hidden"><div class="w-72 px-3 py-4 bg-white shadow rounded-lg text-left"><h3 class="px-2 c_h-heading c_h-heading--caps text-sm tracking-wider text-gray-600 pb-2">Monitoring features</h3><ul><li><a href="https://www.appsignal.com/tour/errors/" class="flex items-center space-x-3 px-2 py-1 no-underline rounded transition-colors group"><div class="text-orange-500 flex justify-center items-center h-7 w-7 border border-gray-200 bg-white shadow-sm rounded-md group-hover:shadow transition-all"><svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="bug" class="svg-inline--fa fa-bug fa-xs " role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M256 0c53 0 96 43 96 96v3.6c0 15.7-12.7 28.4-28.4 28.4H188.4c-15.7 0-28.4-12.7-28.4-28.4V96c0-53 43-96 96-96zM41.4 105.4c12.5-12.5 32.8-12.5 45.3 0l64 64c.7 .7 1.3 1.4 1.9 2.1c14.2-7.3 30.4-11.4 47.5-11.4H312c17.1 0 33.2 4.1 47.5 11.4c.6-.7 1.2-1.4 1.9-2.1l64-64c12.5-12.5 32.8-12.5 45.3 0s12.5 32.8 0 45.3l-64 64c-.7 .7-1.4 1.3-2.1 1.9c6.2 12 10.1 25.3 11.1 39.5H480c17.7 0 32 14.3 32 32s-14.3 32-32 32H416c0 24.6-5.5 47.8-15.4 68.6c2.2 1.3 4.2 2.9 6 4.8l64 64c12.5 12.5 12.5 32.8 0 45.3s-32.8 12.5-45.3 0l-63.1-63.1c-24.5 21.8-55.8 36.2-90.3 39.6V240c0-8.8-7.2-16-16-16s-16 7.2-16 16V479.2c-34.5-3.4-65.8-17.8-90.3-39.6L86.6 502.6c-12.5 12.5-32.8 12.5-45.3 0s-12.5-32.8 0-45.3l64-64c1.9-1.9 3.9-3.4 6-4.8C101.5 367.8 96 344.6 96 320H32c-17.7 0-32-14.3-32-32s14.3-32 32-32H96.3c1.1-14.1 5-27.5 11.1-39.5c-.7-.6-1.4-1.2-2.1-1.9l-64-64c-12.5-12.5-12.5-32.8 0-45.3z"></path></svg></div><span class="text-gray-700 group-hover:text-gray-800">Error tracking</span></a></li><li><a href="https://www.appsignal.com/tour/performance" class="flex items-center space-x-3 px-2 py-1 no-underline rounded transition-colors group"><div class="text-green-500 flex justify-center items-center h-7 w-7 border border-gray-200 bg-white shadow-sm rounded-md group-hover:shadow transition-all"><svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="rabbit-running" class="svg-inline--fa fa-rabbit-running fa-xs " role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512"><path fill="currentColor" d="M460.7 39.3l-2.2-2.1c-4.7-4.6-11.5-6.4-17.9-4.7s-11.4 6.6-13.1 13l-.8 2.9c-3.3 11.8-5.1 23.7-5.5 35.6c24.3 20.6 42.9 47.8 53.3 78.8c-8.2-1.9-16.5-2.9-25-2.9c-.6 0-1.1 0-1.7 0c-18.4-44-56.5-77.4-103.6-89.3l-3.4-.9c-6.2-1.6-12.9-.6-18.5 2.6c-10.8 6.2-15.6 19.5-11.2 31.1c14.9 39.5 44.3 71.4 81.4 89.7c-3.7 6.6-6.4 14-7.6 21.8L279.7 154.9C248.8 137.3 213.8 128 178.2 128c-32.3 0-62.2 16.8-78.9 44.4C89 159.9 73.4 152 56 152c-30.9 0-56 25.1-56 56s25.1 56 56 56c11.3 0 21.8-3.3 30.6-9.1c4.9 17.2 14.5 33.1 28.4 45.9L257.2 431.2C269 442 284.4 448 300.4 448H416c17.7 0 32-14.3 32-32s-14.3-32-32-32H352 335.2 320V340.5c0-42.9-28.4-80.5-69.6-92.3l-30.8-8.8c-8.5-2.4-13.4-11.3-11-19.8s11.3-13.4 19.8-11l30.8 8.8c55 15.7 92.8 65.9 92.8 123.1v15.7l56-32.4 6.4-3.7H515.7c33.3 0 60.3-27 60.3-60.3c0-18.1-8.2-35.3-22.2-46.7l-34.6-28.2c-4.6-3.7-9.4-7.1-14.5-10c8.4-49.3-7.8-100-43.9-135.5zm-318.5 382c-14.7 9.8-18.7 29.7-8.9 44.4s29.7 18.7 44.4 8.9l46-30.7-48.6-44.5-32.9 22zM480 240a16 16 0 1 1 32 0 16 16 0 1 1 -32 0z"></path></svg></div><span class="text-gray-700 group-hover:text-gray-800">Performance monitoring</span></a></li><li><a href="https://www.appsignal.com/tour/hosts" class="flex items-center space-x-3 px-2 py-1 no-underline rounded transition-colors group"><div class="text-teal-500 flex justify-center items-center h-7 w-7 border border-gray-200 bg-white shadow-sm rounded-md group-hover:shadow transition-all"><svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="server" class="svg-inline--fa fa-server fa-xs " role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M64 32C28.7 32 0 60.7 0 96v64c0 35.3 28.7 64 64 64H448c35.3 0 64-28.7 64-64V96c0-35.3-28.7-64-64-64H64zm280 72a24 24 0 1 1 0 48 24 24 0 1 1 0-48zm48 24a24 24 0 1 1 48 0 24 24 0 1 1 -48 0zM64 288c-35.3 0-64 28.7-64 64v64c0 35.3 28.7 64 64 64H448c35.3 0 64-28.7 64-64V352c0-35.3-28.7-64-64-64H64zm280 72a24 24 0 1 1 0 48 24 24 0 1 1 0-48zm56 24a24 24 0 1 1 48 0 24 24 0 1 1 -48 0z"></path></svg></div><span class="text-gray-700 group-hover:text-gray-800">Host monitoring</span></a></li><li><a href="https://www.appsignal.com/tour/anomaly-detection" class="flex items-center space-x-3 px-2 py-1 no-underline rounded transition-colors group"><div class="text-red-500 flex justify-center items-center h-7 w-7 border border-gray-200 bg-white shadow-sm rounded-md group-hover:shadow transition-all"><svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="siren-on" class="svg-inline--fa fa-siren-on fa-xs " role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 512"><path fill="currentColor" d="M69.3 36l48 32c11 7.4 14 22.3 6.7 33.3s-22.3 14-33.3 6.7l-48-32c-11-7.4-14-22.3-6.7-33.3s22.3-14 33.3-6.7zM597.3 76l-48 32c-11 7.4-25.9 4.4-33.3-6.7s-4.4-25.9 6.7-33.3l48-32c11-7.4 25.9-4.4 33.3 6.7s4.4 25.9-6.7 33.3zM24 192H88c13.3 0 24 10.7 24 24s-10.7 24-24 24H24c-13.3 0-24-10.7-24-24s10.7-24 24-24zm528 0h64c13.3 0 24 10.7 24 24s-10.7 24-24 24H552c-13.3 0-24-10.7-24-24s10.7-24 24-24zM144 352l25-199.9c4-32 31.2-56.1 63.5-56.1h175c32.3 0 59.5 24 63.5 56.1L496 352H234.1l21.8-174c1.1-8.8-5.1-16.8-13.9-17.9s-16.8 5.1-17.9 13.9L201.9 352H144zM96 416c0-17.7 14.3-32 32-32H512c17.7 0 32 14.3 32 32v32c0 17.7-14.3 32-32 32H128c-17.7 0-32-14.3-32-32V416z"></path></svg></div><span class="text-gray-700 group-hover:text-gray-800">Anomaly detection</span></a></li><li><a href="https://www.appsignal.com/tour/uptime-monitoring" class="flex items-center space-x-3 px-2 py-1 no-underline rounded transition-colors group"><div class="text-yellow-500 flex justify-center items-center h-7 w-7 border border-gray-200 bg-white shadow-sm rounded-md group-hover:shadow transition-all"><svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="traffic-light" class="svg-inline--fa fa-traffic-light fa-xs " role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 320 512"><path fill="currentColor" d="M64 0C28.7 0 0 28.7 0 64V352c0 88.4 71.6 160 160 160s160-71.6 160-160V64c0-35.3-28.7-64-64-64H64zm96 416a48 48 0 1 1 0-96 48 48 0 1 1 0 96zm48-176a48 48 0 1 1 -96 0 48 48 0 1 1 96 0zm-48-80a48 48 0 1 1 0-96 48 48 0 1 1 0 96z"></path></svg></div><span class="text-gray-700 group-hover:text-gray-800">Uptime monitoring</span></a></li><li><a href="https://www.appsignal.com/tour/metrics" class="flex items-center space-x-3 px-2 py-1 no-underline rounded transition-colors group"><div class="text-purple-500 flex justify-center items-center h-7 w-7 border border-gray-200 bg-white shadow-sm rounded-md group-hover:shadow transition-all"><svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="gauge-high" class="svg-inline--fa fa-gauge-high fa-xs " role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M0 256a256 256 0 1 1 512 0A256 256 0 1 1 0 256zM288 96a32 32 0 1 0 -64 0 32 32 0 1 0 64 0zM256 416c35.3 0 64-28.7 64-64c0-17.4-6.9-33.1-18.1-44.6L366 161.7c5.3-12.1-.2-26.3-12.3-31.6s-26.3 .2-31.6 12.3L257.9 288c-.6 0-1.3 0-1.9 0c-35.3 0-64 28.7-64 64s28.7 64 64 64zM176 144a32 32 0 1 0 -64 0 32 32 0 1 0 64 0zM96 288a32 32 0 1 0 0-64 32 32 0 1 0 0 64zm352-32a32 32 0 1 0 -64 0 32 32 0 1 0 64 0z"></path></svg></div><span class="text-gray-700 group-hover:text-gray-800">Metric dashboards</span></a></li><li><a href="https://www.appsignal.com/tour/workflow" class="flex items-center space-x-3 px-2 py-1 no-underline rounded transition-colors group"><div class="text-blue-500 flex justify-center items-center h-7 w-7 border border-gray-200 bg-white shadow-sm rounded-md group-hover:shadow transition-all"><svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="users" class="svg-inline--fa fa-users fa-xs " role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 512"><path fill="currentColor" d="M144 0a80 80 0 1 1 0 160A80 80 0 1 1 144 0zM512 0a80 80 0 1 1 0 160A80 80 0 1 1 512 0zM0 298.7C0 239.8 47.8 192 106.7 192h42.7c15.9 0 31 3.5 44.6 9.7c-1.3 7.2-1.9 14.7-1.9 22.3c0 38.2 16.8 72.5 43.3 96c-.2 0-.4 0-.7 0H21.3C9.6 320 0 310.4 0 298.7zM405.3 320c-.2 0-.4 0-.7 0c26.6-23.5 43.3-57.8 43.3-96c0-7.6-.7-15-1.9-22.3c13.6-6.3 28.7-9.7 44.6-9.7h42.7C592.2 192 640 239.8 640 298.7c0 11.8-9.6 21.3-21.3 21.3H405.3zM224 224a96 96 0 1 1 192 0 96 96 0 1 1 -192 0zM128 485.3C128 411.7 187.7 352 261.3 352H378.7C452.3 352 512 411.7 512 485.3c0 14.7-11.9 26.7-26.7 26.7H154.7c-14.7 0-26.7-11.9-26.7-26.7z"></path></svg></div><span class="text-gray-700 group-hover:text-gray-800">Workflow</span></a></li><li><a href="https://www.appsignal.com/tour/log-management" class="flex items-center space-x-3 px-2 py-1 no-underline rounded transition-colors group"><div class="text-hot-pink-500 flex justify-center items-center h-7 w-7 border border-gray-200 bg-white shadow-sm rounded-md group-hover:shadow transition-all"><svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="line-columns" class="svg-inline--fa fa-line-columns fa-xs " role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M224 64c0-17.7-14.3-32-32-32H32C14.3 32 0 46.3 0 64S14.3 96 32 96H192c17.7 0 32-14.3 32-32zm0 128c0-17.7-14.3-32-32-32H32c-17.7 0-32 14.3-32 32s14.3 32 32 32H192c17.7 0 32-14.3 32-32zM0 320c0 17.7 14.3 32 32 32H192c17.7 0 32-14.3 32-32s-14.3-32-32-32H32c-17.7 0-32 14.3-32 32zM224 448c0-17.7-14.3-32-32-32H32c-17.7 0-32 14.3-32 32s14.3 32 32 32H192c17.7 0 32-14.3 32-32zM288 64c0 17.7 14.3 32 32 32H480c17.7 0 32-14.3 32-32s-14.3-32-32-32H320c-17.7 0-32 14.3-32 32zM512 192c0-17.7-14.3-32-32-32H320c-17.7 0-32 14.3-32 32s14.3 32 32 32H480c17.7 0 32-14.3 32-32zM288 320c0 17.7 14.3 32 32 32H480c17.7 0 32-14.3 32-32s-14.3-32-32-32H320c-17.7 0-32 14.3-32 32zM512 448c0-17.7-14.3-32-32-32H320c-17.7 0-32 14.3-32 32s14.3 32 32 32H480c17.7 0 32-14.3 32-32z"></path></svg></div><span class="text-gray-700 group-hover:text-gray-800">Log management</span></a></li><li><a href="https://www.appsignal.com/tour/automated-dashboards" class="flex items-center space-x-3 px-2 py-1 no-underline rounded transition-colors group"><div class="text-purple-500 flex justify-center items-center h-7 w-7 border border-gray-200 bg-white shadow-sm rounded-md group-hover:shadow transition-all"><svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="gauge-high" class="svg-inline--fa fa-gauge-high fa-xs " role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M0 256a256 256 0 1 1 512 0A256 256 0 1 1 0 256zM288 96a32 32 0 1 0 -64 0 32 32 0 1 0 64 0zM256 416c35.3 0 64-28.7 64-64c0-17.4-6.9-33.1-18.1-44.6L366 161.7c5.3-12.1-.2-26.3-12.3-31.6s-26.3 .2-31.6 12.3L257.9 288c-.6 0-1.3 0-1.9 0c-35.3 0-64 28.7-64 64s28.7 64 64 64zM176 144a32 32 0 1 0 -64 0 32 32 0 1 0 64 0zM96 288a32 32 0 1 0 0-64 32 32 0 1 0 0 64zm352-32a32 32 0 1 0 -64 0 32 32 0 1 0 64 0z"></path></svg></div><span class="text-gray-700 group-hover:text-gray-800">Automated Dashboards</span></a></li><li><a href="https://www.appsignal.com/tour/check-ins" class="flex items-center space-x-3 px-2 py-1 no-underline rounded transition-colors group"><div class="text-blue-500 flex justify-center items-center h-7 w-7 border border-gray-200 bg-white shadow-sm rounded-md group-hover:shadow transition-all"><svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="square-check" class="svg-inline--fa fa-square-check fa-xs " role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><path fill="currentColor" d="M64 32C28.7 32 0 60.7 0 96V416c0 35.3 28.7 64 64 64H384c35.3 0 64-28.7 64-64V96c0-35.3-28.7-64-64-64H64zM337 209L209 337c-9.4 9.4-24.6 9.4-33.9 0l-64-64c-9.4-9.4-9.4-24.6 0-33.9s24.6-9.4 33.9 0l47 47L303 175c9.4-9.4 24.6-9.4 33.9 0s9.4 24.6 0 33.9z"></path></svg></div><span class="text-gray-700 group-hover:text-gray-800">Check-ins</span></a></li><li><a href="https://www.appsignal.com/tour/time-detective" class="flex items-center space-x-3 px-2 py-1 no-underline rounded transition-colors group"><div class="text-azure-500 flex justify-center items-center h-7 w-7 border border-gray-200 bg-white shadow-sm rounded-md group-hover:shadow transition-all"></div><span class="text-gray-700 group-hover:text-gray-800">Time Detective</span></a></li></ul></div></div></div></li><li><div class="block py-2 px-2 xl:px-3 no-underline cursor-pointer select-none text-gray-200 hover:text-white"><span>Languages</span><svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="angle-down" class="svg-inline--fa fa-angle-down inline-block pb-0.5 ml-2 text-gray-400 text-xxs" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><path fill="currentColor" d="M201.4 374.6c12.5 12.5 32.8 12.5 45.3 0l160-160c12.5-12.5 12.5-32.8 0-45.3s-32.8-12.5-45.3 0L224 306.7 86.6 169.4c-12.5-12.5-32.8-12.5-45.3 0s-12.5 32.8 0 45.3l160 160z"></path></svg><div class="absolute mt-0 pt-2.5 z-10 w-72 hidden"><div class="w-72 px-3 py-4 bg-white shadow rounded-lg text-left"><h3 class="px-2 c_h-heading c_h-heading--caps text-sm tracking-wider text-gray-600 pb-2">Supported Languages</h3><ul><li><a href="https://www.appsignal.com/ruby" class="flex items-center space-x-3 px-2 py-1 no-underline rounded transition-colors group"><div class="text-red-500 flex justify-center items-center h-7 w-7 border border-gray-200 bg-white shadow-sm rounded-md group-hover:shadow transition-all"><svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="gem" class="svg-inline--fa fa-gem fa-xs " role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M116.7 33.8c4.5-6.1 11.7-9.8 19.3-9.8H376c7.6 0 14.8 3.6 19.3 9.8l112 152c6.8 9.2 6.1 21.9-1.5 30.4l-232 256c-4.5 5-11 7.9-17.8 7.9s-13.2-2.9-17.8-7.9l-232-256c-7.7-8.5-8.3-21.2-1.5-30.4l112-152zm38.5 39.8c-3.3 2.5-4.2 7-2.1 10.5l57.4 95.6L63.3 192c-4.1 .3-7.3 3.8-7.3 8s3.2 7.6 7.3 8l192 16c.4 0 .9 0 1.3 0l192-16c4.1-.3 7.3-3.8 7.3-8s-3.2-7.6-7.3-8L301.5 179.8l57.4-95.6c2.1-3.5 1.2-8.1-2.1-10.5s-7.9-2-10.7 1L256 172.2 165.9 74.6c-2.8-3-7.4-3.4-10.7-1z"></path></svg></div><span class="text-gray-700 group-hover:text-gray-800">Ruby (on Rails) APM</span></a></li><li><a href="https://www.appsignal.com/elixir" class="flex items-center space-x-3 px-2 py-1 no-underline rounded transition-colors group"><div class="text-purple-500 flex justify-center items-center h-7 w-7 border border-gray-200 bg-white shadow-sm rounded-md group-hover:shadow transition-all"><svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="droplet" class="svg-inline--fa fa-droplet fa-xs " role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 384 512"><path fill="currentColor" d="M192 512C86 512 0 426 0 320C0 228.8 130.2 57.7 166.6 11.7C172.6 4.2 181.5 0 191.1 0h1.8c9.6 0 18.5 4.2 24.5 11.7C253.8 57.7 384 228.8 384 320c0 106-86 192-192 192zM96 336c0-8.8-7.2-16-16-16s-16 7.2-16 16c0 61.9 50.1 112 112 112c8.8 0 16-7.2 16-16s-7.2-16-16-16c-44.2 0-80-35.8-80-80z"></path></svg></div><span class="text-gray-700 group-hover:text-gray-800">Elixir APM</span></a></li><li><a href="https://www.appsignal.com/nodejs" class="flex items-center space-x-3 px-2 py-1 no-underline rounded transition-colors group"><div class="text-green-500 flex justify-center items-center h-7 w-7 border border-gray-200 bg-white shadow-sm rounded-md group-hover:shadow transition-all"><svg aria-hidden="true" focusable="false" data-prefix="fab" data-icon="node-js" class="svg-inline--fa fa-node-js fa-xs " role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><path fill="currentColor" d="M224 508c-6.7 0-13.5-1.8-19.4-5.2l-61.7-36.5c-9.2-5.2-4.7-7-1.7-8 12.3-4.3 14.8-5.2 27.9-12.7 1.4-.8 3.2-.5 4.6.4l47.4 28.1c1.7 1 4.1 1 5.7 0l184.7-106.6c1.7-1 2.8-3 2.8-5V149.3c0-2.1-1.1-4-2.9-5.1L226.8 37.7c-1.7-1-4-1-5.7 0L36.6 144.3c-1.8 1-2.9 3-2.9 5.1v213.1c0 2 1.1 4 2.9 4.9l50.6 29.2c27.5 13.7 44.3-2.4 44.3-18.7V167.5c0-3 2.4-5.3 5.4-5.3h23.4c2.9 0 5.4 2.3 5.4 5.3V378c0 36.6-20 57.6-54.7 57.6-10.7 0-19.1 0-42.5-11.6l-48.4-27.9C8.1 389.2.7 376.3.7 362.4V149.3c0-13.8 7.4-26.8 19.4-33.7L204.6 9c11.7-6.6 27.2-6.6 38.8 0l184.7 106.7c12 6.9 19.4 19.8 19.4 33.7v213.1c0 13.8-7.4 26.7-19.4 33.7L243.4 502.8c-5.9 3.4-12.6 5.2-19.4 5.2zm149.1-210.1c0-39.9-27-50.5-83.7-58-57.4-7.6-63.2-11.5-63.2-24.9 0-11.1 4.9-25.9 47.4-25.9 37.9 0 51.9 8.2 57.7 33.8.5 2.4 2.7 4.2 5.2 4.2h24c1.5 0 2.9-.6 3.9-1.7s1.5-2.6 1.4-4.1c-3.7-44.1-33-64.6-92.2-64.6-52.7 0-84.1 22.2-84.1 59.5 0 40.4 31.3 51.6 81.8 56.6 60.5 5.9 65.2 14.8 65.2 26.7 0 20.6-16.6 29.4-55.5 29.4-48.9 0-59.6-12.3-63.2-36.6-.4-2.6-2.6-4.5-5.3-4.5h-23.9c-3 0-5.3 2.4-5.3 5.3 0 31.1 16.9 68.2 97.8 68.2 58.4-.1 92-23.2 92-63.4z"></path></svg></div><span class="text-gray-700 group-hover:text-gray-800">Node.js APM</span></a></li><li><a href="https://www.appsignal.com/javascript" class="flex items-center space-x-3 px-2 py-1 no-underline rounded transition-colors group"><div class="text-yellow-500 flex justify-center items-center h-7 w-7 border border-gray-200 bg-white shadow-sm rounded-md group-hover:shadow transition-all"><svg aria-hidden="true" focusable="false" data-prefix="fab" data-icon="square-js" class="svg-inline--fa fa-square-js fa-xs " role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><path fill="currentColor" d="M448 96c0-35.3-28.7-64-64-64H64C28.7 32 0 60.7 0 96V416c0 35.3 28.7 64 64 64H384c35.3 0 64-28.7 64-64V96zM180.9 444.9c-33.7 0-53.2-17.4-63.2-38.5L152 385.7c6.6 11.7 12.6 21.6 27.1 21.6c13.8 0 22.6-5.4 22.6-26.5V237.7h42.1V381.4c0 43.6-25.6 63.5-62.9 63.5zm85.8-43L301 382.1c9 14.7 20.8 25.6 41.5 25.6c17.4 0 28.6-8.7 28.6-20.8c0-14.4-11.4-19.5-30.7-28l-10.5-4.5c-30.4-12.9-50.5-29.2-50.5-63.5c0-31.6 24.1-55.6 61.6-55.6c26.8 0 46 9.3 59.8 33.7L368 290c-7.2-12.9-15-18-27.1-18c-12.3 0-20.1 7.8-20.1 18c0 12.6 7.8 17.7 25.9 25.6l10.5 4.5c35.8 15.3 55.9 31 55.9 66.2c0 37.8-29.8 58.6-69.7 58.6c-39.1 0-64.4-18.6-76.7-43z"></path></svg></div><span class="text-gray-700 group-hover:text-gray-800">JavaScript Error Tracking</span></a></li><li><a href="https://www.appsignal.com/python" class="flex items-center space-x-3 px-2 py-1 no-underline rounded transition-colors group"><div class="text-blue-500 flex justify-center items-center h-7 w-7 border border-gray-200 bg-white shadow-sm rounded-md group-hover:shadow transition-all"><svg aria-hidden="true" focusable="false" data-prefix="fab" data-icon="python" class="svg-inline--fa fa-python fa-xs " role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><path fill="currentColor" d="M439.8 200.5c-7.7-30.9-22.3-54.2-53.4-54.2h-40.1v47.4c0 36.8-31.2 67.8-66.8 67.8H172.7c-29.2 0-53.4 25-53.4 54.3v101.8c0 29 25.2 46 53.4 54.3 33.8 9.9 66.3 11.7 106.8 0 26.9-7.8 53.4-23.5 53.4-54.3v-40.7H226.2v-13.6h160.2c31.1 0 42.6-21.7 53.4-54.2 11.2-33.5 10.7-65.7 0-108.6zM286.2 404c11.1 0 20.1 9.1 20.1 20.3 0 11.3-9 20.4-20.1 20.4-11 0-20.1-9.2-20.1-20.4.1-11.3 9.1-20.3 20.1-20.3zM167.8 248.1h106.8c29.7 0 53.4-24.5 53.4-54.3V91.9c0-29-24.4-50.7-53.4-55.6-35.8-5.9-74.7-5.6-106.8.1-45.2 8-53.4 24.7-53.4 55.6v40.7h106.9v13.6h-147c-31.1 0-58.3 18.7-66.8 54.2-9.8 40.7-10.2 66.1 0 108.6 7.6 31.6 25.7 54.2 56.8 54.2H101v-48.8c0-35.3 30.5-66.4 66.8-66.4zm-6.7-142.6c-11.1 0-20.1-9.1-20.1-20.3.1-11.3 9-20.4 20.1-20.4 11 0 20.1 9.2 20.1 20.4s-9 20.3-20.1 20.3z"></path></svg></div><span class="text-gray-700 group-hover:text-gray-800">Python APM</span></a></li></ul></div></div></div></li><li><a href="https://www.appsignal.com/learning-center" class="block py-2 px-2 xl:px-3 no-underline text-gray-200 hover:text-white ">Learn</a></li><li><a href="https://docs.appsignal.com" class="block py-2 px-2 xl:px-3 no-underline text-gray-200 hover:text-white ">Docs</a></li><li><a class="block py-2 px-2 xl:px-3 no-underline text-gray-200 hover:text-white " href="/">Blog</a></li><li><a href="https://www.appsignal.com/plans" class="block py-2 px-2 xl:px-3 no-underline text-gray-200 hover:text-white ">Pricing</a></li></ul></div><ul class="hidden xl:flex justify-end space-x-4 dark"><li><a href="https://appsignal.com/users/sign_in" class="c-button c-button--sm text-white border-gray-700 bg-blue-800 hover:bg-gray-700">Login</a></li><li><a href="https://appsignal.com/users/sign_up" class="c-button c-button--sm c-button--green">Start free trial</a></li></ul><div class="xl:hidden dark"><a href="#menu" class="c-button c-button--sm c-button--white dark:c-button--gray"><div class="flex item-center space-x-2 pr-0.5"><div>Menu</div><div class="inline-block w-2 pt-0.25"><svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="bars" class="svg-inline--fa fa-bars " role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><path fill="currentColor" d="M0 96C0 78.3 14.3 64 32 64H416c17.7 0 32 14.3 32 32s-14.3 32-32 32H32C14.3 128 0 113.7 0 96zM0 256c0-17.7 14.3-32 32-32H416c17.7 0 32 14.3 32 32s-14.3 32-32 32H32c-17.7 0-32-14.3-32-32zM448 416c0 17.7-14.3 32-32 32H32c-17.7 0-32-14.3-32-32s14.3-32 32-32H416c17.7 0 32 14.3 32 32z"></path></svg></div></div></a></div></div><nav class="hidden border-t border-gray-700"><div class="dark c-container h-screen overflow-y-auto pt-7 pb-40"><div><h3 class="c_h-heading c_h-heading--caps text-sm text-gray-200 pb-3 mb-3 border-b border-gray-700">Monitoring features</h3><ul class="pb-8 space-y-1"><li><a href="https://www.appsignal.com/tour/errors/" class="flex items-center space-x-3 py-1 no-underline text-sm sm:text-base"><figure class="c_h-icon-box undefined c_h-icon-box--sm undefined"><svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="bug" class="svg-inline--fa fa-bug " role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M256 0c53 0 96 43 96 96v3.6c0 15.7-12.7 28.4-28.4 28.4H188.4c-15.7 0-28.4-12.7-28.4-28.4V96c0-53 43-96 96-96zM41.4 105.4c12.5-12.5 32.8-12.5 45.3 0l64 64c.7 .7 1.3 1.4 1.9 2.1c14.2-7.3 30.4-11.4 47.5-11.4H312c17.1 0 33.2 4.1 47.5 11.4c.6-.7 1.2-1.4 1.9-2.1l64-64c12.5-12.5 32.8-12.5 45.3 0s12.5 32.8 0 45.3l-64 64c-.7 .7-1.4 1.3-2.1 1.9c6.2 12 10.1 25.3 11.1 39.5H480c17.7 0 32 14.3 32 32s-14.3 32-32 32H416c0 24.6-5.5 47.8-15.4 68.6c2.2 1.3 4.2 2.9 6 4.8l64 64c12.5 12.5 12.5 32.8 0 45.3s-32.8 12.5-45.3 0l-63.1-63.1c-24.5 21.8-55.8 36.2-90.3 39.6V240c0-8.8-7.2-16-16-16s-16 7.2-16 16V479.2c-34.5-3.4-65.8-17.8-90.3-39.6L86.6 502.6c-12.5 12.5-32.8 12.5-45.3 0s-12.5-32.8 0-45.3l64-64c1.9-1.9 3.9-3.4 6-4.8C101.5 367.8 96 344.6 96 320H32c-17.7 0-32-14.3-32-32s14.3-32 32-32H96.3c1.1-14.1 5-27.5 11.1-39.5c-.7-.6-1.4-1.2-2.1-1.9l-64-64c-12.5-12.5-12.5-32.8 0-45.3z"></path></svg></figure><span>Error tracking</span></a></li><li><a href="https://www.appsignal.com/tour/performance" class="flex items-center space-x-3 py-1 no-underline text-sm sm:text-base"><figure class="c_h-icon-box undefined c_h-icon-box--sm undefined"><svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="rabbit-running" class="svg-inline--fa fa-rabbit-running " role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512"><path fill="currentColor" d="M460.7 39.3l-2.2-2.1c-4.7-4.6-11.5-6.4-17.9-4.7s-11.4 6.6-13.1 13l-.8 2.9c-3.3 11.8-5.1 23.7-5.5 35.6c24.3 20.6 42.9 47.8 53.3 78.8c-8.2-1.9-16.5-2.9-25-2.9c-.6 0-1.1 0-1.7 0c-18.4-44-56.5-77.4-103.6-89.3l-3.4-.9c-6.2-1.6-12.9-.6-18.5 2.6c-10.8 6.2-15.6 19.5-11.2 31.1c14.9 39.5 44.3 71.4 81.4 89.7c-3.7 6.6-6.4 14-7.6 21.8L279.7 154.9C248.8 137.3 213.8 128 178.2 128c-32.3 0-62.2 16.8-78.9 44.4C89 159.9 73.4 152 56 152c-30.9 0-56 25.1-56 56s25.1 56 56 56c11.3 0 21.8-3.3 30.6-9.1c4.9 17.2 14.5 33.1 28.4 45.9L257.2 431.2C269 442 284.4 448 300.4 448H416c17.7 0 32-14.3 32-32s-14.3-32-32-32H352 335.2 320V340.5c0-42.9-28.4-80.5-69.6-92.3l-30.8-8.8c-8.5-2.4-13.4-11.3-11-19.8s11.3-13.4 19.8-11l30.8 8.8c55 15.7 92.8 65.9 92.8 123.1v15.7l56-32.4 6.4-3.7H515.7c33.3 0 60.3-27 60.3-60.3c0-18.1-8.2-35.3-22.2-46.7l-34.6-28.2c-4.6-3.7-9.4-7.1-14.5-10c8.4-49.3-7.8-100-43.9-135.5zm-318.5 382c-14.7 9.8-18.7 29.7-8.9 44.4s29.7 18.7 44.4 8.9l46-30.7-48.6-44.5-32.9 22zM480 240a16 16 0 1 1 32 0 16 16 0 1 1 -32 0z"></path></svg></figure><span>Performance monitoring</span></a></li><li><a href="https://www.appsignal.com/tour/hosts" class="flex items-center space-x-3 py-1 no-underline text-sm sm:text-base"><figure class="c_h-icon-box undefined c_h-icon-box--sm undefined"><svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="server" class="svg-inline--fa fa-server " role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M64 32C28.7 32 0 60.7 0 96v64c0 35.3 28.7 64 64 64H448c35.3 0 64-28.7 64-64V96c0-35.3-28.7-64-64-64H64zm280 72a24 24 0 1 1 0 48 24 24 0 1 1 0-48zm48 24a24 24 0 1 1 48 0 24 24 0 1 1 -48 0zM64 288c-35.3 0-64 28.7-64 64v64c0 35.3 28.7 64 64 64H448c35.3 0 64-28.7 64-64V352c0-35.3-28.7-64-64-64H64zm280 72a24 24 0 1 1 0 48 24 24 0 1 1 0-48zm56 24a24 24 0 1 1 48 0 24 24 0 1 1 -48 0z"></path></svg></figure><span>Host monitoring</span></a></li><li><a href="https://www.appsignal.com/tour/anomaly-detection" class="flex items-center space-x-3 py-1 no-underline text-sm sm:text-base"><figure class="c_h-icon-box undefined c_h-icon-box--sm undefined"><svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="siren-on" class="svg-inline--fa fa-siren-on " role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 512"><path fill="currentColor" d="M69.3 36l48 32c11 7.4 14 22.3 6.7 33.3s-22.3 14-33.3 6.7l-48-32c-11-7.4-14-22.3-6.7-33.3s22.3-14 33.3-6.7zM597.3 76l-48 32c-11 7.4-25.9 4.4-33.3-6.7s-4.4-25.9 6.7-33.3l48-32c11-7.4 25.9-4.4 33.3 6.7s4.4 25.9-6.7 33.3zM24 192H88c13.3 0 24 10.7 24 24s-10.7 24-24 24H24c-13.3 0-24-10.7-24-24s10.7-24 24-24zm528 0h64c13.3 0 24 10.7 24 24s-10.7 24-24 24H552c-13.3 0-24-10.7-24-24s10.7-24 24-24zM144 352l25-199.9c4-32 31.2-56.1 63.5-56.1h175c32.3 0 59.5 24 63.5 56.1L496 352H234.1l21.8-174c1.1-8.8-5.1-16.8-13.9-17.9s-16.8 5.1-17.9 13.9L201.9 352H144zM96 416c0-17.7 14.3-32 32-32H512c17.7 0 32 14.3 32 32v32c0 17.7-14.3 32-32 32H128c-17.7 0-32-14.3-32-32V416z"></path></svg></figure><span>Anomaly detection</span></a></li><li><a href="https://www.appsignal.com/tour/uptime-monitoring" class="flex items-center space-x-3 py-1 no-underline text-sm sm:text-base"><figure class="c_h-icon-box undefined c_h-icon-box--sm undefined"><svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="traffic-light" class="svg-inline--fa fa-traffic-light " role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 320 512"><path fill="currentColor" d="M64 0C28.7 0 0 28.7 0 64V352c0 88.4 71.6 160 160 160s160-71.6 160-160V64c0-35.3-28.7-64-64-64H64zm96 416a48 48 0 1 1 0-96 48 48 0 1 1 0 96zm48-176a48 48 0 1 1 -96 0 48 48 0 1 1 96 0zm-48-80a48 48 0 1 1 0-96 48 48 0 1 1 0 96z"></path></svg></figure><span>Uptime monitoring</span></a></li><li><a href="https://www.appsignal.com/tour/metrics" class="flex items-center space-x-3 py-1 no-underline text-sm sm:text-base"><figure class="c_h-icon-box undefined c_h-icon-box--sm undefined"><svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="gauge-high" class="svg-inline--fa fa-gauge-high " role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M0 256a256 256 0 1 1 512 0A256 256 0 1 1 0 256zM288 96a32 32 0 1 0 -64 0 32 32 0 1 0 64 0zM256 416c35.3 0 64-28.7 64-64c0-17.4-6.9-33.1-18.1-44.6L366 161.7c5.3-12.1-.2-26.3-12.3-31.6s-26.3 .2-31.6 12.3L257.9 288c-.6 0-1.3 0-1.9 0c-35.3 0-64 28.7-64 64s28.7 64 64 64zM176 144a32 32 0 1 0 -64 0 32 32 0 1 0 64 0zM96 288a32 32 0 1 0 0-64 32 32 0 1 0 0 64zm352-32a32 32 0 1 0 -64 0 32 32 0 1 0 64 0z"></path></svg></figure><span>Metric dashboards</span></a></li><li><a href="https://www.appsignal.com/tour/workflow" class="flex items-center space-x-3 py-1 no-underline text-sm sm:text-base"><figure class="c_h-icon-box undefined c_h-icon-box--sm undefined"><svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="users" class="svg-inline--fa fa-users " role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 512"><path fill="currentColor" d="M144 0a80 80 0 1 1 0 160A80 80 0 1 1 144 0zM512 0a80 80 0 1 1 0 160A80 80 0 1 1 512 0zM0 298.7C0 239.8 47.8 192 106.7 192h42.7c15.9 0 31 3.5 44.6 9.7c-1.3 7.2-1.9 14.7-1.9 22.3c0 38.2 16.8 72.5 43.3 96c-.2 0-.4 0-.7 0H21.3C9.6 320 0 310.4 0 298.7zM405.3 320c-.2 0-.4 0-.7 0c26.6-23.5 43.3-57.8 43.3-96c0-7.6-.7-15-1.9-22.3c13.6-6.3 28.7-9.7 44.6-9.7h42.7C592.2 192 640 239.8 640 298.7c0 11.8-9.6 21.3-21.3 21.3H405.3zM224 224a96 96 0 1 1 192 0 96 96 0 1 1 -192 0zM128 485.3C128 411.7 187.7 352 261.3 352H378.7C452.3 352 512 411.7 512 485.3c0 14.7-11.9 26.7-26.7 26.7H154.7c-14.7 0-26.7-11.9-26.7-26.7z"></path></svg></figure><span>Workflow</span></a></li><li><a href="https://www.appsignal.com/tour/log-management" class="flex items-center space-x-3 py-1 no-underline text-sm sm:text-base"><figure class="c_h-icon-box undefined c_h-icon-box--sm undefined"><svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="line-columns" class="svg-inline--fa fa-line-columns " role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M224 64c0-17.7-14.3-32-32-32H32C14.3 32 0 46.3 0 64S14.3 96 32 96H192c17.7 0 32-14.3 32-32zm0 128c0-17.7-14.3-32-32-32H32c-17.7 0-32 14.3-32 32s14.3 32 32 32H192c17.7 0 32-14.3 32-32zM0 320c0 17.7 14.3 32 32 32H192c17.7 0 32-14.3 32-32s-14.3-32-32-32H32c-17.7 0-32 14.3-32 32zM224 448c0-17.7-14.3-32-32-32H32c-17.7 0-32 14.3-32 32s14.3 32 32 32H192c17.7 0 32-14.3 32-32zM288 64c0 17.7 14.3 32 32 32H480c17.7 0 32-14.3 32-32s-14.3-32-32-32H320c-17.7 0-32 14.3-32 32zM512 192c0-17.7-14.3-32-32-32H320c-17.7 0-32 14.3-32 32s14.3 32 32 32H480c17.7 0 32-14.3 32-32zM288 320c0 17.7 14.3 32 32 32H480c17.7 0 32-14.3 32-32s-14.3-32-32-32H320c-17.7 0-32 14.3-32 32zM512 448c0-17.7-14.3-32-32-32H320c-17.7 0-32 14.3-32 32s14.3 32 32 32H480c17.7 0 32-14.3 32-32z"></path></svg></figure><span>Log management</span></a></li><li><a href="https://www.appsignal.com/tour/automated-dashboards" class="flex items-center space-x-3 py-1 no-underline text-sm sm:text-base"><figure class="c_h-icon-box undefined c_h-icon-box--sm undefined"><svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="gauge-high" class="svg-inline--fa fa-gauge-high " role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M0 256a256 256 0 1 1 512 0A256 256 0 1 1 0 256zM288 96a32 32 0 1 0 -64 0 32 32 0 1 0 64 0zM256 416c35.3 0 64-28.7 64-64c0-17.4-6.9-33.1-18.1-44.6L366 161.7c5.3-12.1-.2-26.3-12.3-31.6s-26.3 .2-31.6 12.3L257.9 288c-.6 0-1.3 0-1.9 0c-35.3 0-64 28.7-64 64s28.7 64 64 64zM176 144a32 32 0 1 0 -64 0 32 32 0 1 0 64 0zM96 288a32 32 0 1 0 0-64 32 32 0 1 0 0 64zm352-32a32 32 0 1 0 -64 0 32 32 0 1 0 64 0z"></path></svg></figure><span>Automated Dashboards</span></a></li><li><a href="https://www.appsignal.com/tour/check-ins" class="flex items-center space-x-3 py-1 no-underline text-sm sm:text-base"><figure class="c_h-icon-box undefined c_h-icon-box--sm undefined"><svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="square-check" class="svg-inline--fa fa-square-check " role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><path fill="currentColor" d="M64 32C28.7 32 0 60.7 0 96V416c0 35.3 28.7 64 64 64H384c35.3 0 64-28.7 64-64V96c0-35.3-28.7-64-64-64H64zM337 209L209 337c-9.4 9.4-24.6 9.4-33.9 0l-64-64c-9.4-9.4-9.4-24.6 0-33.9s24.6-9.4 33.9 0l47 47L303 175c9.4-9.4 24.6-9.4 33.9 0s9.4 24.6 0 33.9z"></path></svg></figure><span>Check-ins</span></a></li><li><a href="https://www.appsignal.com/tour/time-detective" class="flex items-center space-x-3 py-1 no-underline text-sm sm:text-base"><figure class="c_h-icon-box undefined c_h-icon-box--sm undefined"></figure><span>Time Detective</span></a></li></ul></div><div><h3 class="c_h-heading c_h-heading--caps text-sm text-gray-200 pb-3 mb-3 border-b border-gray-700">Supported Languages</h3><ul class="pb-8 space-y-1"><li><a href="https://www.appsignal.com/ruby" class="flex items-center space-x-3 py-1 no-underline text-sm sm:text-base"><figure class="c_h-icon-box undefined c_h-icon-box--sm undefined"><svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="gem" class="svg-inline--fa fa-gem " role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M116.7 33.8c4.5-6.1 11.7-9.8 19.3-9.8H376c7.6 0 14.8 3.6 19.3 9.8l112 152c6.8 9.2 6.1 21.9-1.5 30.4l-232 256c-4.5 5-11 7.9-17.8 7.9s-13.2-2.9-17.8-7.9l-232-256c-7.7-8.5-8.3-21.2-1.5-30.4l112-152zm38.5 39.8c-3.3 2.5-4.2 7-2.1 10.5l57.4 95.6L63.3 192c-4.1 .3-7.3 3.8-7.3 8s3.2 7.6 7.3 8l192 16c.4 0 .9 0 1.3 0l192-16c4.1-.3 7.3-3.8 7.3-8s-3.2-7.6-7.3-8L301.5 179.8l57.4-95.6c2.1-3.5 1.2-8.1-2.1-10.5s-7.9-2-10.7 1L256 172.2 165.9 74.6c-2.8-3-7.4-3.4-10.7-1z"></path></svg></figure><span>Ruby (on Rails) APM</span></a></li><li><a href="https://www.appsignal.com/elixir" class="flex items-center space-x-3 py-1 no-underline text-sm sm:text-base"><figure class="c_h-icon-box undefined c_h-icon-box--sm undefined"><svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="droplet" class="svg-inline--fa fa-droplet " role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 384 512"><path fill="currentColor" d="M192 512C86 512 0 426 0 320C0 228.8 130.2 57.7 166.6 11.7C172.6 4.2 181.5 0 191.1 0h1.8c9.6 0 18.5 4.2 24.5 11.7C253.8 57.7 384 228.8 384 320c0 106-86 192-192 192zM96 336c0-8.8-7.2-16-16-16s-16 7.2-16 16c0 61.9 50.1 112 112 112c8.8 0 16-7.2 16-16s-7.2-16-16-16c-44.2 0-80-35.8-80-80z"></path></svg></figure><span>Elixir APM</span></a></li><li><a href="https://www.appsignal.com/nodejs" class="flex items-center space-x-3 py-1 no-underline text-sm sm:text-base"><figure class="c_h-icon-box undefined c_h-icon-box--sm undefined"><svg aria-hidden="true" focusable="false" data-prefix="fab" data-icon="node-js" class="svg-inline--fa fa-node-js " role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><path fill="currentColor" d="M224 508c-6.7 0-13.5-1.8-19.4-5.2l-61.7-36.5c-9.2-5.2-4.7-7-1.7-8 12.3-4.3 14.8-5.2 27.9-12.7 1.4-.8 3.2-.5 4.6.4l47.4 28.1c1.7 1 4.1 1 5.7 0l184.7-106.6c1.7-1 2.8-3 2.8-5V149.3c0-2.1-1.1-4-2.9-5.1L226.8 37.7c-1.7-1-4-1-5.7 0L36.6 144.3c-1.8 1-2.9 3-2.9 5.1v213.1c0 2 1.1 4 2.9 4.9l50.6 29.2c27.5 13.7 44.3-2.4 44.3-18.7V167.5c0-3 2.4-5.3 5.4-5.3h23.4c2.9 0 5.4 2.3 5.4 5.3V378c0 36.6-20 57.6-54.7 57.6-10.7 0-19.1 0-42.5-11.6l-48.4-27.9C8.1 389.2.7 376.3.7 362.4V149.3c0-13.8 7.4-26.8 19.4-33.7L204.6 9c11.7-6.6 27.2-6.6 38.8 0l184.7 106.7c12 6.9 19.4 19.8 19.4 33.7v213.1c0 13.8-7.4 26.7-19.4 33.7L243.4 502.8c-5.9 3.4-12.6 5.2-19.4 5.2zm149.1-210.1c0-39.9-27-50.5-83.7-58-57.4-7.6-63.2-11.5-63.2-24.9 0-11.1 4.9-25.9 47.4-25.9 37.9 0 51.9 8.2 57.7 33.8.5 2.4 2.7 4.2 5.2 4.2h24c1.5 0 2.9-.6 3.9-1.7s1.5-2.6 1.4-4.1c-3.7-44.1-33-64.6-92.2-64.6-52.7 0-84.1 22.2-84.1 59.5 0 40.4 31.3 51.6 81.8 56.6 60.5 5.9 65.2 14.8 65.2 26.7 0 20.6-16.6 29.4-55.5 29.4-48.9 0-59.6-12.3-63.2-36.6-.4-2.6-2.6-4.5-5.3-4.5h-23.9c-3 0-5.3 2.4-5.3 5.3 0 31.1 16.9 68.2 97.8 68.2 58.4-.1 92-23.2 92-63.4z"></path></svg></figure><span>Node.js APM</span></a></li><li><a href="https://www.appsignal.com/javascript" class="flex items-center space-x-3 py-1 no-underline text-sm sm:text-base"><figure class="c_h-icon-box undefined c_h-icon-box--sm undefined"><svg aria-hidden="true" focusable="false" data-prefix="fab" data-icon="square-js" class="svg-inline--fa fa-square-js " role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><path fill="currentColor" d="M448 96c0-35.3-28.7-64-64-64H64C28.7 32 0 60.7 0 96V416c0 35.3 28.7 64 64 64H384c35.3 0 64-28.7 64-64V96zM180.9 444.9c-33.7 0-53.2-17.4-63.2-38.5L152 385.7c6.6 11.7 12.6 21.6 27.1 21.6c13.8 0 22.6-5.4 22.6-26.5V237.7h42.1V381.4c0 43.6-25.6 63.5-62.9 63.5zm85.8-43L301 382.1c9 14.7 20.8 25.6 41.5 25.6c17.4 0 28.6-8.7 28.6-20.8c0-14.4-11.4-19.5-30.7-28l-10.5-4.5c-30.4-12.9-50.5-29.2-50.5-63.5c0-31.6 24.1-55.6 61.6-55.6c26.8 0 46 9.3 59.8 33.7L368 290c-7.2-12.9-15-18-27.1-18c-12.3 0-20.1 7.8-20.1 18c0 12.6 7.8 17.7 25.9 25.6l10.5 4.5c35.8 15.3 55.9 31 55.9 66.2c0 37.8-29.8 58.6-69.7 58.6c-39.1 0-64.4-18.6-76.7-43z"></path></svg></figure><span>JavaScript Error Tracking</span></a></li><li><a href="https://www.appsignal.com/python" class="flex items-center space-x-3 py-1 no-underline text-sm sm:text-base"><figure class="c_h-icon-box undefined c_h-icon-box--sm undefined"><svg aria-hidden="true" focusable="false" data-prefix="fab" data-icon="python" class="svg-inline--fa fa-python " role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><path fill="currentColor" d="M439.8 200.5c-7.7-30.9-22.3-54.2-53.4-54.2h-40.1v47.4c0 36.8-31.2 67.8-66.8 67.8H172.7c-29.2 0-53.4 25-53.4 54.3v101.8c0 29 25.2 46 53.4 54.3 33.8 9.9 66.3 11.7 106.8 0 26.9-7.8 53.4-23.5 53.4-54.3v-40.7H226.2v-13.6h160.2c31.1 0 42.6-21.7 53.4-54.2 11.2-33.5 10.7-65.7 0-108.6zM286.2 404c11.1 0 20.1 9.1 20.1 20.3 0 11.3-9 20.4-20.1 20.4-11 0-20.1-9.2-20.1-20.4.1-11.3 9.1-20.3 20.1-20.3zM167.8 248.1h106.8c29.7 0 53.4-24.5 53.4-54.3V91.9c0-29-24.4-50.7-53.4-55.6-35.8-5.9-74.7-5.6-106.8.1-45.2 8-53.4 24.7-53.4 55.6v40.7h106.9v13.6h-147c-31.1 0-58.3 18.7-66.8 54.2-9.8 40.7-10.2 66.1 0 108.6 7.6 31.6 25.7 54.2 56.8 54.2H101v-48.8c0-35.3 30.5-66.4 66.8-66.4zm-6.7-142.6c-11.1 0-20.1-9.1-20.1-20.3.1-11.3 9-20.4 20.1-20.4 11 0 20.1 9.2 20.1 20.4s-9 20.3-20.1 20.3z"></path></svg></figure><span>Python APM</span></a></li></ul></div><div></div><div></div><div></div><div></div><div><ul><li><a href="https://www.appsignal.com/learning-center" class="flex items-center space-x-3 py-1 no-underline text-sm sm:text-base"><figure class="c_h-icon-box undefined c_h-icon-box--sm undefined"><svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="book-open" class="svg-inline--fa fa-book-open " role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512"><path fill="currentColor" d="M249.6 471.5c10.8 3.8 22.4-4.1 22.4-15.5V78.6c0-4.2-1.6-8.4-5-11C247.4 52 202.4 32 144 32C93.5 32 46.3 45.3 18.1 56.1C6.8 60.5 0 71.7 0 83.8V454.1c0 11.9 12.8 20.2 24.1 16.5C55.6 460.1 105.5 448 144 448c33.9 0 79 14 105.6 23.5zm76.8 0C353 462 398.1 448 432 448c38.5 0 88.4 12.1 119.9 22.6c11.3 3.8 24.1-4.6 24.1-16.5V83.8c0-12.1-6.8-23.3-18.1-27.6C529.7 45.3 482.5 32 432 32c-58.4 0-103.4 20-123 35.6c-3.3 2.6-5 6.8-5 11V456c0 11.4 11.7 19.3 22.4 15.5z"></path></svg></figure><span>Learn</span></a></li><li><a href="https://docs.appsignal.com" class="flex items-center space-x-3 py-1 no-underline text-sm sm:text-base"><figure class="c_h-icon-box undefined c_h-icon-box--sm undefined"><svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="book" class="svg-inline--fa fa-book " role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><path fill="currentColor" d="M96 0C43 0 0 43 0 96V416c0 53 43 96 96 96H384h32c17.7 0 32-14.3 32-32s-14.3-32-32-32V384c17.7 0 32-14.3 32-32V32c0-17.7-14.3-32-32-32H384 96zm0 384H352v64H96c-17.7 0-32-14.3-32-32s14.3-32 32-32zm32-240c0-8.8 7.2-16 16-16H336c8.8 0 16 7.2 16 16s-7.2 16-16 16H144c-8.8 0-16-7.2-16-16zm16 48H336c8.8 0 16 7.2 16 16s-7.2 16-16 16H144c-8.8 0-16-7.2-16-16s7.2-16 16-16z"></path></svg></figure><span>Docs</span></a></li><li><a class="flex items-center space-x-3 py-1 no-underline text-sm sm:text-base" href="/"><figure class="c_h-icon-box undefined c_h-icon-box--sm undefined"><svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="rss" class="svg-inline--fa fa-rss " role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><path fill="currentColor" d="M0 64C0 46.3 14.3 32 32 32c229.8 0 416 186.2 416 416c0 17.7-14.3 32-32 32s-32-14.3-32-32C384 253.6 226.4 96 32 96C14.3 96 0 81.7 0 64zM0 416a64 64 0 1 1 128 0A64 64 0 1 1 0 416zM32 160c159.1 0 288 128.9 288 288c0 17.7-14.3 32-32 32s-32-14.3-32-32c0-123.7-100.3-224-224-224c-17.7 0-32-14.3-32-32s14.3-32 32-32z"></path></svg></figure><span>Blog</span></a></li><li><a href="https://www.appsignal.com/plans" class="flex items-center space-x-3 py-1 no-underline text-sm sm:text-base"><figure class="c_h-icon-box undefined c_h-icon-box--sm undefined"><svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="dollar-sign" class="svg-inline--fa fa-dollar-sign " role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 320 512"><path fill="currentColor" d="M160 0c17.7 0 32 14.3 32 32V67.7c1.6 .2 3.1 .4 4.7 .7c.4 .1 .7 .1 1.1 .2l48 8.8c17.4 3.2 28.9 19.9 25.7 37.2s-19.9 28.9-37.2 25.7l-47.5-8.7c-31.3-4.6-58.9-1.5-78.3 6.2s-27.2 18.3-29 28.1c-2 10.7-.5 16.7 1.2 20.4c1.8 3.9 5.5 8.3 12.8 13.2c16.3 10.7 41.3 17.7 73.7 26.3l2.9 .8c28.6 7.6 63.6 16.8 89.6 33.8c14.2 9.3 27.6 21.9 35.9 39.5c8.5 17.9 10.3 37.9 6.4 59.2c-6.9 38-33.1 63.4-65.6 76.7c-13.7 5.6-28.6 9.2-44.4 11V480c0 17.7-14.3 32-32 32s-32-14.3-32-32V445.1c-.4-.1-.9-.1-1.3-.2l-.2 0 0 0c-24.4-3.8-64.5-14.3-91.5-26.3c-16.1-7.2-23.4-26.1-16.2-42.2s26.1-23.4 42.2-16.2c20.9 9.3 55.3 18.5 75.2 21.6c31.9 4.7 58.2 2 76-5.3c16.9-6.9 24.6-16.9 26.8-28.9c1.9-10.6 .4-16.7-1.3-20.4c-1.9-4-5.6-8.4-13-13.3c-16.4-10.7-41.5-17.7-74-26.3l-2.8-.7 0 0C119.4 279.3 84.4 270 58.4 253c-14.2-9.3-27.5-22-35.8-39.6c-8.4-17.9-10.1-37.9-6.1-59.2C23.7 116 52.3 91.2 84.8 78.3c13.3-5.3 27.9-8.9 43.2-11V32c0-17.7 14.3-32 32-32z"></path></svg></figure><span>Pricing</span></a></li><li><a href="https://appsignal.com/users/sign_in" class="block mt-4 text-center c-button c-button--sm c-button--gray">Login</a></li><li><a href="https://appsignal.com/users/sign_up" class="block mt-4 text-center c-button c-button--sm">Start free trial</a></li></ul></div></div></nav></header><main><article><header class="bg-purple-800"><div class="c-container"><div class="grid grid-cols-1 items-center gap-12 lg:gap-8 mx-auto py-12 md:py-16 lg:grid-cols-2 max-w-6xl"><div><p class="c_h-heading c_h-heading--caps text-base md:text-lg mb-3 text-purple-200">elixir</p><h1 class="c_h-heading c_h-heading--4xl sm:c_h-heading--5xl md:c_h-heading--6xl text-white mb-6 md:mb-8">How to Use gRPC in Elixir</h1><div class="flex items-center space-x-2.5"><div class="flex -space-x-2"><figure class="relative z-20 w-6 md:w-8 h-6 md:h-8 rounded-full overflow-hidden border-2 border-white"><img alt="Alex Koutmos" loading="lazy" decoding="async" data-nimg="fill" class="object-cover" style="position:absolute;height:100%;width:100%;left:0;top:0;right:0;bottom:0;color:transparent" sizes="24px" srcSet="/_next/image?url=%2Fimages%2Fauthors%2Falex.jpg&amp;w=16&amp;q=75 16w, /_next/image?url=%2Fimages%2Fauthors%2Falex.jpg&amp;w=32&amp;q=75 32w, /_next/image?url=%2Fimages%2Fauthors%2Falex.jpg&amp;w=48&amp;q=75 48w, /_next/image?url=%2Fimages%2Fauthors%2Falex.jpg&amp;w=64&amp;q=75 64w, /_next/image?url=%2Fimages%2Fauthors%2Falex.jpg&amp;w=96&amp;q=75 96w, /_next/image?url=%2Fimages%2Fauthors%2Falex.jpg&amp;w=128&amp;q=75 128w, /_next/image?url=%2Fimages%2Fauthors%2Falex.jpg&amp;w=256&amp;q=75 256w, /_next/image?url=%2Fimages%2Fauthors%2Falex.jpg&amp;w=384&amp;q=75 384w, /_next/image?url=%2Fimages%2Fauthors%2Falex.jpg&amp;w=640&amp;q=75 640w, /_next/image?url=%2Fimages%2Fauthors%2Falex.jpg&amp;w=750&amp;q=75 750w, /_next/image?url=%2Fimages%2Fauthors%2Falex.jpg&amp;w=828&amp;q=75 828w, /_next/image?url=%2Fimages%2Fauthors%2Falex.jpg&amp;w=1080&amp;q=75 1080w, /_next/image?url=%2Fimages%2Fauthors%2Falex.jpg&amp;w=1200&amp;q=75 1200w, /_next/image?url=%2Fimages%2Fauthors%2Falex.jpg&amp;w=1920&amp;q=75 1920w, /_next/image?url=%2Fimages%2Fauthors%2Falex.jpg&amp;w=2048&amp;q=75 2048w, /_next/image?url=%2Fimages%2Fauthors%2Falex.jpg&amp;w=3840&amp;q=75 3840w" src="/_next/image?url=%2Fimages%2Fauthors%2Falex.jpg&amp;w=3840&amp;q=75"/></figure></div><div class="flex flex-col"><p class="sm:text-base text-purple-200"><a class="no-underline hover:underline" href="/authors/alex-koutmos.html">Alex Koutmos</a><span class="white-space-nowrap"> <!-- -->on <time dateTime="2020-03-24">Mar 24, 2020</time></span></p></div></div></div><div class="rounded-lg overflow-hidden border-2 border-white border-opacity-20"><div class="bg-purple-700 relative h-48 sm:h-64 md:h-80"><img alt="How to Use gRPC in Elixir" decoding="async" data-nimg="fill" class="object-cover" style="position:absolute;height:100%;width:100%;left:0;top:0;right:0;bottom:0;color:transparent" sizes="(min-width: 1024px) 1200px, 100vw" srcSet="/_next/image?url=%2Fimages%2Fblog%2F2020-03%2Fhow-to-use-grpc-in-elixir.jpg&amp;w=640&amp;q=50 640w, /_next/image?url=%2Fimages%2Fblog%2F2020-03%2Fhow-to-use-grpc-in-elixir.jpg&amp;w=750&amp;q=50 750w, /_next/image?url=%2Fimages%2Fblog%2F2020-03%2Fhow-to-use-grpc-in-elixir.jpg&amp;w=828&amp;q=50 828w, /_next/image?url=%2Fimages%2Fblog%2F2020-03%2Fhow-to-use-grpc-in-elixir.jpg&amp;w=1080&amp;q=50 1080w, /_next/image?url=%2Fimages%2Fblog%2F2020-03%2Fhow-to-use-grpc-in-elixir.jpg&amp;w=1200&amp;q=50 1200w, /_next/image?url=%2Fimages%2Fblog%2F2020-03%2Fhow-to-use-grpc-in-elixir.jpg&amp;w=1920&amp;q=50 1920w, /_next/image?url=%2Fimages%2Fblog%2F2020-03%2Fhow-to-use-grpc-in-elixir.jpg&amp;w=2048&amp;q=50 2048w, /_next/image?url=%2Fimages%2Fblog%2F2020-03%2Fhow-to-use-grpc-in-elixir.jpg&amp;w=3840&amp;q=50 3840w" src="/_next/image?url=%2Fimages%2Fblog%2F2020-03%2Fhow-to-use-grpc-in-elixir.jpg&amp;w=3840&amp;q=50"/></div></div></div></div></header><div class="c-container pt-16 pb-12"><div class="prose md:prose-md mx-auto"><p>In today&#x27;s post, we&#x27;ll learn what gRPC is, when you should reach for such a tool, and some of the pros and cons of using it. After going over an introduction of gRPC, we&#x27;ll dive right into a sample application where we&#x27;ll build an Elixir backend API powered by gRPC.</p> <p>Let&#x27;s jump right in!</p> <h2 id="what-is-grpc-and-how-does-it-work">What Is gRPC and How Does It Work?</h2> <p>gRPC is a framework used to enable a remote procedure call (RPC) style of communication. RPC is a style of system communication where a client can directly invoke exposed methods on a server. From the client&#x27;s perspective, it feels no different than making a call to a local function or method as long as you provide the applicable parameters and handle the return type appropriately. gRPC facilitates this communication by providing 2 things for you:</p> <ol> <li>A client library which can be used to invoke allowed procedures</li> <li>A consistent data serialization standard via Protocol Buffers</li> </ol> <p>Let&#x27;s break these two items down so we can appreciate the inner workings of gRPC. When creating a gRPC server, you&#x27;ll have to define what procedures are invokable from the client, what inputs they accept, and what outputs they return. This interface specification is what allows client libraries (generally called gRPC stubs) to be automatically generated for various languages and runtimes as the contract for the remote procedure call is explicitly defined. This gRPC client library can then communicate with the server using Protocol Buffers. Protocol Buffers provide a mechanism for serializing and deserializing the payloads (both request and response) so that you can operate on the data with types native to your language. The diagram available in the gRPC documentation can help visualize <a href="https://grpc.io/docs/guides/">this interaction</a>:</p> <p><figure class="mx-auto" style="max-width:552"><div data-rmiz-wrap="visible" style="display:block"><img alt="gRPC Diagram" loading="lazy" width="552" height="327" decoding="async" data-nimg="1" style="color:transparent" src="/images/blog/2020-03/landing-2.svg"/><button aria-label="Zoom image" data-rmiz-btn-open="true"></button></div></figure> <em>Source: <a href="https://grpc.io">https://grpc.io</a></em></p> <p>One thing that we haven&#x27;t discussed yet is how data is transmitted between the client and the server. For this, gRPC leans on the HTTP/2 protocol. By using HTTP/2 as the underlying protocol, gRPC is able to support features such as bi-directional data streaming and several other features that are not available in HTTP/1.1.</p> <h2 id="when-would-you-use-grpc-over-restgraphql">When Would You Use gRPC Over REST/GraphQL?</h2> <p>The obvious question that may come to mind is: &quot;How does gRPC compare to REST/GraphQL, and when do I use one over the other?&quot;.</p> <p>In general, if you plan to use gRPC for a frontend application, there are a couple of caveats that you need to keep in mind. In order to serialize and deserialize Protocol Buffer payloads from your Javascript application, you&#x27;ll have to leverage <a href="https://github.com/grpc/grpc-web">grpc-web</a>. In addition, you&#x27;ll also need to run a proxy on the backend (the default supported gRPC proxy is Envoy) since browsers cannot talk directly to gRPC servers. Depending on your resources and time constraints, this may be a show stopper; in which case, REST and GraphQL will do just fine.</p> <p>If frontend application communication is not a requirement, and instead what you require is inter-microservice communication from within your service cluster, then the barrier to entry for gRPC gets lowered considerably. At the time of writing, gRPC currently has client libraries and tooling for most mainstream languages including Elixir, Python, C++, Go, Ruby, to name a few. I would argue that the barrier to entry for consuming a RESTful API is still much lower than consuming a gRPC service given that all you need for the former is an HTTP client, which is baked into most languages and runtimes these days.</p> <p>On the other hand, if you are willing to make the investment, you do get the added benefits of having your responses and requests checked against the Protocol Buffer specification that is used for code generation. This, in turn, provides you with some guarantees as to what you can expect on the client-side and the server-side. This guarantee and introspection is also something that you get with GraphQL when you define your server schemas. An added benefit of GraphQL over gRPC is that you are able to dynamically request embedded properties from within your schema depending on the query that you make to your backend server.</p> <p>Like most things in the software engineering field, the technology you choose will largely depend on your application. Below are my personal TL;DR rules of thumb regarding the various technologies:</p> <p>gRPC:</p> <ul> <li>Use: When communicating between microservices in my service cluster or if performance is a requirement</li> <li>Don&#x27;t use: When I need to transmit data from the browser to the backend</li> </ul> <p>GraphQL:</p> <ul> <li>Use: When I need to aggregate data from multiple microservices for the purposes of streamlining frontend development, or if my frontend data requirements are dynamic</li> <li>Don&#x27;t use: When communicating between microservices in my service cluster, or if my API needs to be used by the lowest common denominator of consumers</li> </ul> <p>REST:</p> <ul> <li>Use: When I need to put something together quickly or if I need to cater to the lowest common denominator of consumers</li> <li>Don&#x27;t use: If I require any kind of type checking or if I want to reduce payload sizes over the wire</li> </ul> <h2 id="experimenting-with-grpc-in-elixir">Experimenting With gRPC in Elixir</h2> <p>With all the theory out of the way, it&#x27;s time to get our hands dirty and experiment with wiring up a gRPC server. In order to keep us focused on the gRPC experience, we&#x27;ll opt for having a backend powered by an Agent versus an actual database, but all of the concepts should be easily transferable to an application backed by Postgres, for example. Our gRPC application will be a simple user management service where we can create and fetch users. After creating our Elixir service, we&#x27;ll interact with it via grpcurl, which is effectively cURL, but for gRPC. If at any point you get stuck or require the source code, feel free to <a href="https://github.com/akoutmos/sample_app">check it out on GitHub</a>. With all that being said, let&#x27;s dive right in!</p> <p>Before creating our Elixir project, there are a couple of things that we require on our machine in order to properly develop and test our application. We&#x27;ll first need to install <code>protoc</code> so that <code>.proto</code> files can be compiled appropriately. If you are on an OSX machine, you can run <code>brew install protobuf</code>, otherwise, <a href="https://github.com/protocolbuffers/protobuf/blob/master/src/README.md">see instructions</a> specific to your platform. Now, with <code>protoc</code> available on your machine, you&#x27;ll also want to install <code>grpcurl</code> so that you can interact with the application. Once again, if you are on an OSX machine, you can run <code>brew install grpcurl</code>, otherwise, <a href="https://github.com/fullstorydev/grpcurl#installation">check for instructions specific to your platform</a>.</p> <aside class="not-prose my-8 sm:my-10 md:-mx-12"><div><p class="md:pl-10 mb-4 text-gray-600 leading-none"><span class="w-4 pr-1 text-center">↓</span> Article continues below</p></div><div class="relative overflow-hidden text-white bg-gradient-to-b from-gray-900 to-azure-700 sm:to-gray-900 border border-gray-200 rounded-lg"><div class="flex sm:bg-[radial-gradient(#0A1530, transparent)]"><div class="z-0 relative w-full"><div class="z-0 absolute hidden sm:block bg-cover w-[194px] h-[123px] -right-8 sm:-right-8 md:right-auto md:left-0 top-6 md:top-6"><img role="presentation" alt="Left squiggle" loading="lazy" decoding="async" data-nimg="fill" style="position:absolute;height:100%;width:100%;left:0;top:0;right:0;bottom:0;color:transparent" sizes="100vw" srcSet="/_next/image?url=%2Fimages%2Fcomponents%2Fbanner%2Fimg-left%402x.png&amp;w=640&amp;q=75 640w, /_next/image?url=%2Fimages%2Fcomponents%2Fbanner%2Fimg-left%402x.png&amp;w=750&amp;q=75 750w, /_next/image?url=%2Fimages%2Fcomponents%2Fbanner%2Fimg-left%402x.png&amp;w=828&amp;q=75 828w, /_next/image?url=%2Fimages%2Fcomponents%2Fbanner%2Fimg-left%402x.png&amp;w=1080&amp;q=75 1080w, /_next/image?url=%2Fimages%2Fcomponents%2Fbanner%2Fimg-left%402x.png&amp;w=1200&amp;q=75 1200w, /_next/image?url=%2Fimages%2Fcomponents%2Fbanner%2Fimg-left%402x.png&amp;w=1920&amp;q=75 1920w, /_next/image?url=%2Fimages%2Fcomponents%2Fbanner%2Fimg-left%402x.png&amp;w=2048&amp;q=75 2048w, /_next/image?url=%2Fimages%2Fcomponents%2Fbanner%2Fimg-left%402x.png&amp;w=3840&amp;q=75 3840w" src="/_next/image?url=%2Fimages%2Fcomponents%2Fbanner%2Fimg-left%402x.png&amp;w=3840&amp;q=75"/></div></div><div class="relative min-w-[300px] sm:min-w-[380px] px-4 py-10 mx-auto space-y-4 text-center"><h4 class="z-20 relative c_h-heading c_h-heading--xl sm:c_h-heading--2xl"><span class="block">Is your app broken or slow?</span> <span class="block">AppSignal lets you know.</span></h4><a href="https://www.appsignal.com/elixir" class="z-20 relative c-button c-button--sm c-button--green text-sm">Monitoring by AppSignal →</a></div><div class="z-0 relative w-full"><div class="z-0 absolute hidden sm:block bg-cover w-[168px] h-[136px] -left-8 sm:-left-4 md:left-auto md:right-0 -bottom-14 sm:bottom-0 md:bottom-0"><img role="presentation" alt="Right squiggle" loading="lazy" decoding="async" data-nimg="fill" style="position:absolute;height:100%;width:100%;left:0;top:0;right:0;bottom:0;color:transparent" sizes="100vw" srcSet="/_next/image?url=%2Fimages%2Fcomponents%2Fbanner%2Fimg-right%402x.png&amp;w=640&amp;q=75 640w, /_next/image?url=%2Fimages%2Fcomponents%2Fbanner%2Fimg-right%402x.png&amp;w=750&amp;q=75 750w, /_next/image?url=%2Fimages%2Fcomponents%2Fbanner%2Fimg-right%402x.png&amp;w=828&amp;q=75 828w, /_next/image?url=%2Fimages%2Fcomponents%2Fbanner%2Fimg-right%402x.png&amp;w=1080&amp;q=75 1080w, /_next/image?url=%2Fimages%2Fcomponents%2Fbanner%2Fimg-right%402x.png&amp;w=1200&amp;q=75 1200w, /_next/image?url=%2Fimages%2Fcomponents%2Fbanner%2Fimg-right%402x.png&amp;w=1920&amp;q=75 1920w, /_next/image?url=%2Fimages%2Fcomponents%2Fbanner%2Fimg-right%402x.png&amp;w=2048&amp;q=75 2048w, /_next/image?url=%2Fimages%2Fcomponents%2Fbanner%2Fimg-right%402x.png&amp;w=3840&amp;q=75 3840w" src="/_next/image?url=%2Fimages%2Fcomponents%2Fbanner%2Fimg-right%402x.png&amp;w=3840&amp;q=75"/></div></div></div></div></aside> <p>Lastly, you&#x27;ll want to run <code>mix escript.install hex protobuf</code> and ensure that <code>protoc-gen-elixir</code> script is available on your path (if you use ASDF as your runtime version manager, this requires running <code>asdf reshim elixir</code>). With all that boilerplate done, you can run <code>mix new sample_app --sup</code> to get a new application started.</p> <p>Once inside your sample application directory, you&#x27;ll want to update your <code>mix.exs</code> file to include our gRPC related dependencies. For this application, we will be leveraging <a href="https://github.com/elixir-grpc/grpc">https://github.com/elixir-grpc/grpc</a> and <a href="https://github.com/tony612/protobuf-elixir">https://github.com/tony612/protobuf-elixir</a>. In order to bring these two dependencies into your project, ensure that your <code>deps/0</code> function looks like this:</p> <figure data-rehype-pretty-code-figure=""><div class="relative my-8 group-[.is-breakout]:md:my-10 group-[.is-breakout]:md:-mx-12" data-rehype-pretty-code-fragment="true"><div class="group-[.code-header-hidden]:hidden absolute top-0 inset-x-0 bg-gray-800 text-white rounded-t-lg overflow-hidden"><div class="flex items-center justify-between pr-4 font-mono text-sm"><div class="h-10"><div class="group-[.is-code-tabs]:hidden flex items-center h-10 px-4 sm:px-6 bg-gray-900">Elixir</div></div><div class="flex items-center"><button class="group/svg group-[.is-code-tabs]:invisible sm:group-[.is-code-tabs]:visible w-6 h-6 pr-1 -mr-1 flex justify-end items-center hover:cursor-pointer"><svg width="13" height="15" viewBox="0 0 13 15" fill="none" xmlns="http://www.w3.org/2000/svg"><path class="transition group-hover/svg:fill-white" d="M8.75 13H4.15625C3.30859 13 2.625 12.3164 2.625 11.4688V3.375H0.65625C0.273438 3.375 0 3.67578 0 4.03125V14.0938C0 14.4766 0.273438 14.75 0.65625 14.75H8.09375C8.44922 14.75 8.75 14.4766 8.75 14.0938V13ZM8.75 3.59375V0.75H4.15625C3.77344 0.75 3.5 1.05078 3.5 1.40625V11.4688C3.5 11.8516 3.77344 12.125 4.15625 12.125H11.5938C11.9492 12.125 12.25 11.8516 12.25 11.4688V4.25H9.40625C9.02344 4.25 8.75 3.97656 8.75 3.59375ZM12.0312 2.74609L10.2539 0.96875C10.1172 0.832031 9.95312 0.75 9.78906 0.75H9.625V3.375H12.25V3.21094C12.25 3.04688 12.168 2.88281 12.0312 2.74609Z" fill="#BFC6D6"></path></svg></button></div></div></div><pre tabindex="0" data-language="elixir" data-theme="github-dark" data-raw="defp deps do [ {:grpc, &quot;~&gt; 0.5.0-beta&quot;}, {:cowlib, &quot;~&gt; 2.8.0&quot;, hex: :grpc_cowlib, override: true} ] end " class="z-0 !m-0 !pl-4 dark:bg-gray-100"><div class="sm:px-2 pt-10 group-[.code-header-hidden]:pt-0"><code data-language="elixir" data-theme="github-dark" style="display:grid"><span data-line=""><span style="color:#F97583">defp</span><span style="color:#B392F0"> deps</span><span style="color:#F97583"> do</span></span> <span data-line=""><span style="color:#E1E4E8"> [</span></span> <span data-line=""><span style="color:#E1E4E8"> {</span><span style="color:#79B8FF">:grpc</span><span style="color:#E1E4E8">, </span><span style="color:#9ECBFF">&quot;~&gt; 0.5.0-beta&quot;</span><span style="color:#E1E4E8">},</span></span> <span data-line=""><span style="color:#E1E4E8"> {</span><span style="color:#79B8FF">:cowlib</span><span style="color:#E1E4E8">, </span><span style="color:#9ECBFF">&quot;~&gt; 2.8.0&quot;</span><span style="color:#E1E4E8">, </span><span style="color:#79B8FF">hex:</span><span style="color:#79B8FF"> :grpc_cowlib</span><span style="color:#E1E4E8">, </span><span style="color:#79B8FF">override:</span><span style="color:#79B8FF"> true</span><span style="color:#E1E4E8">}</span></span> <span data-line=""><span style="color:#E1E4E8"> ]</span></span> <span data-line=""><span style="color:#F97583">end</span></span></code></div></pre></div></figure> <p>With that in place, run <code>mix deps.get</code> from the terminal to pull down all necessary project dependencies. Next, you&#x27;ll want to create a configuration file at <code>config/config.exs</code> with the following content:</p> <figure data-rehype-pretty-code-figure=""><div class="relative my-8 group-[.is-breakout]:md:my-10 group-[.is-breakout]:md:-mx-12" data-rehype-pretty-code-fragment="true"><div class="group-[.code-header-hidden]:hidden absolute top-0 inset-x-0 bg-gray-800 text-white rounded-t-lg overflow-hidden"><div class="flex items-center justify-between pr-4 font-mono text-sm"><div class="h-10"><div class="group-[.is-code-tabs]:hidden flex items-center h-10 px-4 sm:px-6 bg-gray-900">Elixir</div></div><div class="flex items-center"><button class="group/svg group-[.is-code-tabs]:invisible sm:group-[.is-code-tabs]:visible w-6 h-6 pr-1 -mr-1 flex justify-end items-center hover:cursor-pointer"><svg width="13" height="15" viewBox="0 0 13 15" fill="none" xmlns="http://www.w3.org/2000/svg"><path class="transition group-hover/svg:fill-white" d="M8.75 13H4.15625C3.30859 13 2.625 12.3164 2.625 11.4688V3.375H0.65625C0.273438 3.375 0 3.67578 0 4.03125V14.0938C0 14.4766 0.273438 14.75 0.65625 14.75H8.09375C8.44922 14.75 8.75 14.4766 8.75 14.0938V13ZM8.75 3.59375V0.75H4.15625C3.77344 0.75 3.5 1.05078 3.5 1.40625V11.4688C3.5 11.8516 3.77344 12.125 4.15625 12.125H11.5938C11.9492 12.125 12.25 11.8516 12.25 11.4688V4.25H9.40625C9.02344 4.25 8.75 3.97656 8.75 3.59375ZM12.0312 2.74609L10.2539 0.96875C10.1172 0.832031 9.95312 0.75 9.78906 0.75H9.625V3.375H12.25V3.21094C12.25 3.04688 12.168 2.88281 12.0312 2.74609Z" fill="#BFC6D6"></path></svg></button></div></div></div><pre tabindex="0" data-language="elixir" data-theme="github-dark" data-raw="use Mix.Config # Configures Elixir&#x27;s Logger config :logger, :console, format: &quot;$time $metadata[$level] $message\n&quot; config :grpc, start_server: true " class="z-0 !m-0 !pl-4 dark:bg-gray-100"><div class="sm:px-2 pt-10 group-[.code-header-hidden]:pt-0"><code data-language="elixir" data-theme="github-dark" style="display:grid"><span data-line=""><span style="color:#F97583">use</span><span style="color:#B392F0"> Mix</span><span style="color:#E1E4E8">.</span><span style="color:#B392F0">Config</span></span> <span data-line=""> </span> <span data-line=""><span style="color:#6A737D"># Configures Elixir&#x27;s Logger</span></span> <span data-line=""><span style="color:#E1E4E8">config </span><span style="color:#79B8FF">:logger</span><span style="color:#E1E4E8">, </span><span style="color:#79B8FF">:console</span><span style="color:#E1E4E8">, </span><span style="color:#79B8FF">format:</span><span style="color:#9ECBFF"> &quot;$time $metadata[$level] $message</span><span style="color:#79B8FF">\n</span><span style="color:#9ECBFF">&quot;</span></span> <span data-line=""> </span> <span data-line=""><span style="color:#E1E4E8">config </span><span style="color:#79B8FF">:grpc</span><span style="color:#E1E4E8">, </span><span style="color:#79B8FF">start_server:</span><span style="color:#79B8FF"> true</span></span></code></div></pre></div></figure> <p>Next, we&#x27;ll want to create the required Protocol Buffer definitions for our application. The <a href="https://developers.google.com/protocol-buffers/docs/proto3">Protocol Buffer specification</a> is fairly large and we&#x27;ll only be using a small subset of it to keep things simple. Create a file <code>sample_app.proto</code> at the root of your project with the following content:</p> <div class="relative my-8 group-[.is-breakout]:md:my-10 group-[.is-breakout]:md:-mx-12" data-rehype-pretty-code-fragment="true"><div class="group-[.code-header-hidden]:hidden absolute top-0 inset-x-0 bg-gray-800 text-white rounded-t-lg overflow-hidden"><div class="flex items-center justify-between pr-4 font-mono text-sm"><div class="h-10"><div class="group-[.is-code-tabs]:hidden flex items-center h-10 px-4 sm:px-6 bg-gray-900">Shell</div></div><div class="flex items-center"><button class="group/svg group-[.is-code-tabs]:invisible sm:group-[.is-code-tabs]:visible w-6 h-6 pr-1 -mr-1 flex justify-end items-center hover:cursor-pointer"><svg width="13" height="15" viewBox="0 0 13 15" fill="none" xmlns="http://www.w3.org/2000/svg"><path class="transition group-hover/svg:fill-white" d="M8.75 13H4.15625C3.30859 13 2.625 12.3164 2.625 11.4688V3.375H0.65625C0.273438 3.375 0 3.67578 0 4.03125V14.0938C0 14.4766 0.273438 14.75 0.65625 14.75H8.09375C8.44922 14.75 8.75 14.4766 8.75 14.0938V13ZM8.75 3.59375V0.75H4.15625C3.77344 0.75 3.5 1.05078 3.5 1.40625V11.4688C3.5 11.8516 3.77344 12.125 4.15625 12.125H11.5938C11.9492 12.125 12.25 11.8516 12.25 11.4688V4.25H9.40625C9.02344 4.25 8.75 3.97656 8.75 3.59375ZM12.0312 2.74609L10.2539 0.96875C10.1172 0.832031 9.95312 0.75 9.78906 0.75H9.625V3.375H12.25V3.21094C12.25 3.04688 12.168 2.88281 12.0312 2.74609Z" fill="#BFC6D6"></path></svg></button></div></div></div><pre data-raw="syntax = &quot;proto3&quot;; package sample_app; service User { rpc Create (CreateRequest) returns (UserReply) {} rpc Get (GetRequest) returns (UserReply) {} } message UserReply { int32 id = 1; string first_name = 2; string last_name = 3; int32 age = 4; } message CreateRequest { string first_name = 1; string last_name = 2; int32 age = 3; } message GetRequest { int32 id = 1; } " class="z-0 !m-0 !pl-4 dark:bg-gray-100"><div class="sm:px-2 pt-10 group-[.code-header-hidden]:pt-0"><code>syntax = &quot;proto3&quot;; package sample_app; service User { rpc Create (CreateRequest) returns (UserReply) {} rpc Get (GetRequest) returns (UserReply) {} } message UserReply { int32 id = 1; string first_name = 2; string last_name = 3; int32 age = 4; } message CreateRequest { string first_name = 1; string last_name = 2; int32 age = 3; } message GetRequest { int32 id = 1; } </code></div></pre></div> <p>As you can see, our Protocol Buffer definition is fairly straightforward and easy to read. We define a service that exposes two RPC methods—<code>Create</code> and <code>Get</code>. We also define the types that each of those RPC calls takes as input and returns as a result. With the <code>sample_app.proto</code> file in place, we&#x27;ll want to open up a terminal and run the following:</p> <figure data-rehype-pretty-code-figure=""><div class="relative my-8 group-[.is-breakout]:md:my-10 group-[.is-breakout]:md:-mx-12" data-rehype-pretty-code-fragment="true"><div class="group-[.code-header-hidden]:hidden absolute top-0 inset-x-0 bg-gray-800 text-white rounded-t-lg overflow-hidden"><div class="flex items-center justify-between pr-4 font-mono text-sm"><div class="h-10"><div class="group-[.is-code-tabs]:hidden flex items-center h-10 px-4 sm:px-6 bg-gray-900">Shell</div></div><div class="flex items-center"><button class="group/svg group-[.is-code-tabs]:invisible sm:group-[.is-code-tabs]:visible w-6 h-6 pr-1 -mr-1 flex justify-end items-center hover:cursor-pointer"><svg width="13" height="15" viewBox="0 0 13 15" fill="none" xmlns="http://www.w3.org/2000/svg"><path class="transition group-hover/svg:fill-white" d="M8.75 13H4.15625C3.30859 13 2.625 12.3164 2.625 11.4688V3.375H0.65625C0.273438 3.375 0 3.67578 0 4.03125V14.0938C0 14.4766 0.273438 14.75 0.65625 14.75H8.09375C8.44922 14.75 8.75 14.4766 8.75 14.0938V13ZM8.75 3.59375V0.75H4.15625C3.77344 0.75 3.5 1.05078 3.5 1.40625V11.4688C3.5 11.8516 3.77344 12.125 4.15625 12.125H11.5938C11.9492 12.125 12.25 11.8516 12.25 11.4688V4.25H9.40625C9.02344 4.25 8.75 3.97656 8.75 3.59375ZM12.0312 2.74609L10.2539 0.96875C10.1172 0.832031 9.95312 0.75 9.78906 0.75H9.625V3.375H12.25V3.21094C12.25 3.04688 12.168 2.88281 12.0312 2.74609Z" fill="#BFC6D6"></path></svg></button></div></div></div><pre tabindex="0" data-language="shell" data-theme="github-dark" data-raw="$ protoc --elixir_out=plugins=grpc:./lib sample_app.proto " class="z-0 !m-0 !pl-4 dark:bg-gray-100"><div class="sm:px-2 pt-10 group-[.code-header-hidden]:pt-0"><code data-language="shell" data-theme="github-dark" style="display:grid"><span data-line=""><span style="color:#B392F0">$</span><span style="color:#9ECBFF"> protoc</span><span style="color:#79B8FF"> --elixir_out=plugins=grpc:./lib</span><span style="color:#9ECBFF"> sample_app.proto</span></span></code></div></pre></div></figure> <p>You&#x27;ll notice that this command produces a file <code>lib/sample_app.pb.ex</code> with several modules within it. If you look carefully, you&#x27;ll see that the code that was generated is the Elixir representation of the <code>sample_app.proto</code> file that we wrote. It contains all of the types that we defined along with the RPC method definitions. With our auto-generated code in place, let&#x27;s get to work on the actual RPC handlers.</p> <p>As previously mentioned, we&#x27;ll be using an Agent to persist state across gRPC calls instead of a database, for the sake of simplicity. Our agent will have the ability to look up users via their ID, and will also be able to create new users. Create a file <code>lib/user_db.ex</code> with the following code which provides that functionality:</p> <figure data-rehype-pretty-code-figure=""><div class="relative my-8 group-[.is-breakout]:md:my-10 group-[.is-breakout]:md:-mx-12" data-rehype-pretty-code-fragment="true"><div class="group-[.code-header-hidden]:hidden absolute top-0 inset-x-0 bg-gray-800 text-white rounded-t-lg overflow-hidden"><div class="flex items-center justify-between pr-4 font-mono text-sm"><div class="h-10"><div class="group-[.is-code-tabs]:hidden flex items-center h-10 px-4 sm:px-6 bg-gray-900">Elixir</div></div><div class="flex items-center"><button class="group/svg group-[.is-code-tabs]:invisible sm:group-[.is-code-tabs]:visible w-6 h-6 pr-1 -mr-1 flex justify-end items-center hover:cursor-pointer"><svg width="13" height="15" viewBox="0 0 13 15" fill="none" xmlns="http://www.w3.org/2000/svg"><path class="transition group-hover/svg:fill-white" d="M8.75 13H4.15625C3.30859 13 2.625 12.3164 2.625 11.4688V3.375H0.65625C0.273438 3.375 0 3.67578 0 4.03125V14.0938C0 14.4766 0.273438 14.75 0.65625 14.75H8.09375C8.44922 14.75 8.75 14.4766 8.75 14.0938V13ZM8.75 3.59375V0.75H4.15625C3.77344 0.75 3.5 1.05078 3.5 1.40625V11.4688C3.5 11.8516 3.77344 12.125 4.15625 12.125H11.5938C11.9492 12.125 12.25 11.8516 12.25 11.4688V4.25H9.40625C9.02344 4.25 8.75 3.97656 8.75 3.59375ZM12.0312 2.74609L10.2539 0.96875C10.1172 0.832031 9.95312 0.75 9.78906 0.75H9.625V3.375H12.25V3.21094C12.25 3.04688 12.168 2.88281 12.0312 2.74609Z" fill="#BFC6D6"></path></svg></button></div></div></div><pre tabindex="0" data-language="elixir" data-theme="github-dark" data-raw="defmodule UserDB do use Agent def start_link(_) do Agent.start_link( fn -&gt; {%{}, 1} end, name: __MODULE__ ) end def add_user(user) do Agent.get_and_update(__MODULE__, fn {users_map, next_id} -&gt; updated_users_map = Map.put(users_map, next_id, user) {Map.put(user, :id, next_id), {updated_users_map, next_id + 1}} end) end def get_user(id) do Agent.get(__MODULE__, fn {users_map, _next_id} -&gt; Map.get(users_map, id) end) end end " class="z-0 !m-0 !pl-4 dark:bg-gray-100"><div class="sm:px-2 pt-10 group-[.code-header-hidden]:pt-0"><code data-language="elixir" data-theme="github-dark" style="display:grid"><span data-line=""><span style="color:#F97583">defmodule</span><span style="color:#B392F0"> UserDB</span><span style="color:#F97583"> do</span></span> <span data-line=""><span style="color:#F97583"> use</span><span style="color:#B392F0"> Agent</span></span> <span data-line=""> </span> <span data-line=""><span style="color:#F97583"> def</span><span style="color:#B392F0"> start_link</span><span style="color:#E1E4E8">(</span><span style="color:#6A737D">_</span><span style="color:#E1E4E8">) </span><span style="color:#F97583">do</span></span> <span data-line=""><span style="color:#B392F0"> Agent</span><span style="color:#E1E4E8">.</span><span style="color:#B392F0">start_link</span><span style="color:#E1E4E8">(</span></span> <span data-line=""><span style="color:#F97583"> fn</span><span style="color:#F97583"> -&gt;</span></span> <span data-line=""><span style="color:#E1E4E8"> {%{}, </span><span style="color:#79B8FF">1</span><span style="color:#E1E4E8">}</span></span> <span data-line=""><span style="color:#F97583"> end</span><span style="color:#E1E4E8">,</span></span> <span data-line=""><span style="color:#79B8FF"> name:</span><span style="color:#79B8FF"> __MODULE__</span></span> <span data-line=""><span style="color:#E1E4E8"> )</span></span> <span data-line=""><span style="color:#F97583"> end</span></span> <span data-line=""> </span> <span data-line=""><span style="color:#F97583"> def</span><span style="color:#B392F0"> add_user</span><span style="color:#E1E4E8">(user) </span><span style="color:#F97583">do</span></span> <span data-line=""><span style="color:#B392F0"> Agent</span><span style="color:#E1E4E8">.</span><span style="color:#B392F0">get_and_update</span><span style="color:#E1E4E8">(</span><span style="color:#79B8FF">__MODULE__</span><span style="color:#E1E4E8">, </span><span style="color:#F97583">fn</span><span style="color:#E1E4E8"> {users_map, next_id} </span><span style="color:#F97583">-&gt;</span></span> <span data-line=""><span style="color:#E1E4E8"> updated_users_map </span><span style="color:#F97583">=</span><span style="color:#B392F0"> Map</span><span style="color:#E1E4E8">.</span><span style="color:#B392F0">put</span><span style="color:#E1E4E8">(users_map, next_id, user)</span></span> <span data-line=""> </span> <span data-line=""><span style="color:#E1E4E8"> {</span><span style="color:#B392F0">Map</span><span style="color:#E1E4E8">.</span><span style="color:#B392F0">put</span><span style="color:#E1E4E8">(user, </span><span style="color:#79B8FF">:id</span><span style="color:#E1E4E8">, next_id), {updated_users_map, next_id </span><span style="color:#F97583">+</span><span style="color:#79B8FF"> 1</span><span style="color:#E1E4E8">}}</span></span> <span data-line=""><span style="color:#F97583"> end</span><span style="color:#E1E4E8">)</span></span> <span data-line=""><span style="color:#F97583"> end</span></span> <span data-line=""> </span> <span data-line=""><span style="color:#F97583"> def</span><span style="color:#B392F0"> get_user</span><span style="color:#E1E4E8">(id) </span><span style="color:#F97583">do</span></span> <span data-line=""><span style="color:#B392F0"> Agent</span><span style="color:#E1E4E8">.</span><span style="color:#B392F0">get</span><span style="color:#E1E4E8">(</span><span style="color:#79B8FF">__MODULE__</span><span style="color:#E1E4E8">, </span><span style="color:#F97583">fn</span><span style="color:#E1E4E8"> {users_map, </span><span style="color:#6A737D">_next_id</span><span style="color:#E1E4E8">} </span><span style="color:#F97583">-&gt;</span></span> <span data-line=""><span style="color:#B392F0"> Map</span><span style="color:#E1E4E8">.</span><span style="color:#B392F0">get</span><span style="color:#E1E4E8">(users_map, id)</span></span> <span data-line=""><span style="color:#F97583"> end</span><span style="color:#E1E4E8">)</span></span> <span data-line=""><span style="color:#F97583"> end</span></span> <span data-line=""><span style="color:#F97583">end</span></span></code></div></pre></div></figure> <p>With that in place, we can create our RPC handlers for creating and getting users. Create a file <code>lib/sample_app.ex</code> with the following content:</p> <figure data-rehype-pretty-code-figure=""><div class="relative my-8 group-[.is-breakout]:md:my-10 group-[.is-breakout]:md:-mx-12" data-rehype-pretty-code-fragment="true"><div class="group-[.code-header-hidden]:hidden absolute top-0 inset-x-0 bg-gray-800 text-white rounded-t-lg overflow-hidden"><div class="flex items-center justify-between pr-4 font-mono text-sm"><div class="h-10"><div class="group-[.is-code-tabs]:hidden flex items-center h-10 px-4 sm:px-6 bg-gray-900">Elixir</div></div><div class="flex items-center"><button class="group/svg group-[.is-code-tabs]:invisible sm:group-[.is-code-tabs]:visible w-6 h-6 pr-1 -mr-1 flex justify-end items-center hover:cursor-pointer"><svg width="13" height="15" viewBox="0 0 13 15" fill="none" xmlns="http://www.w3.org/2000/svg"><path class="transition group-hover/svg:fill-white" d="M8.75 13H4.15625C3.30859 13 2.625 12.3164 2.625 11.4688V3.375H0.65625C0.273438 3.375 0 3.67578 0 4.03125V14.0938C0 14.4766 0.273438 14.75 0.65625 14.75H8.09375C8.44922 14.75 8.75 14.4766 8.75 14.0938V13ZM8.75 3.59375V0.75H4.15625C3.77344 0.75 3.5 1.05078 3.5 1.40625V11.4688C3.5 11.8516 3.77344 12.125 4.15625 12.125H11.5938C11.9492 12.125 12.25 11.8516 12.25 11.4688V4.25H9.40625C9.02344 4.25 8.75 3.97656 8.75 3.59375ZM12.0312 2.74609L10.2539 0.96875C10.1172 0.832031 9.95312 0.75 9.78906 0.75H9.625V3.375H12.25V3.21094C12.25 3.04688 12.168 2.88281 12.0312 2.74609Z" fill="#BFC6D6"></path></svg></button></div></div></div><pre tabindex="0" data-language="elixir" data-theme="github-dark" data-raw="defmodule SampleApp.Endpoint do use GRPC.Endpoint intercept GRPC.Logger.Server run SampleApp.User.Server end defmodule SampleApp.User.Server do use GRPC.Server, service: SampleApp.User.Service def create(request, _stream) do new_user = UserDB.add_user(%{ first_name: request.first_name, last_name: request.last_name, age: request.age }) SampleApp.UserReply.new(new_user) end def get(request, _stream) do user = UserDB.get_user(request.id) if user == nil do raise GRPC.RPCError, status: :not_found else SampleApp.UserReply.new(user) end end end " class="z-0 !m-0 !pl-4 dark:bg-gray-100"><div class="sm:px-2 pt-10 group-[.code-header-hidden]:pt-0"><code data-language="elixir" data-theme="github-dark" style="display:grid"><span data-line=""><span style="color:#F97583">defmodule</span><span style="color:#B392F0"> SampleApp</span><span style="color:#E1E4E8">.</span><span style="color:#B392F0">Endpoint</span><span style="color:#F97583"> do</span></span> <span data-line=""><span style="color:#F97583"> use</span><span style="color:#B392F0"> GRPC</span><span style="color:#E1E4E8">.</span><span style="color:#B392F0">Endpoint</span></span> <span data-line=""> </span> <span data-line=""><span style="color:#E1E4E8"> intercept </span><span style="color:#B392F0">GRPC</span><span style="color:#E1E4E8">.</span><span style="color:#B392F0">Logger</span><span style="color:#E1E4E8">.</span><span style="color:#B392F0">Server</span></span> <span data-line=""><span style="color:#E1E4E8"> run </span><span style="color:#B392F0">SampleApp</span><span style="color:#E1E4E8">.</span><span style="color:#B392F0">User</span><span style="color:#E1E4E8">.</span><span style="color:#B392F0">Server</span></span> <span data-line=""><span style="color:#F97583">end</span></span> <span data-line=""> </span> <span data-line=""><span style="color:#F97583">defmodule</span><span style="color:#B392F0"> SampleApp</span><span style="color:#E1E4E8">.</span><span style="color:#B392F0">User</span><span style="color:#E1E4E8">.</span><span style="color:#B392F0">Server</span><span style="color:#F97583"> do</span></span> <span data-line=""><span style="color:#F97583"> use</span><span style="color:#B392F0"> GRPC</span><span style="color:#E1E4E8">.</span><span style="color:#B392F0">Server</span><span style="color:#E1E4E8">, </span><span style="color:#79B8FF">service:</span><span style="color:#B392F0"> SampleApp</span><span style="color:#E1E4E8">.</span><span style="color:#B392F0">User</span><span style="color:#E1E4E8">.</span><span style="color:#B392F0">Service</span></span> <span data-line=""> </span> <span data-line=""><span style="color:#F97583"> def</span><span style="color:#B392F0"> create</span><span style="color:#E1E4E8">(request, </span><span style="color:#6A737D">_stream</span><span style="color:#E1E4E8">) </span><span style="color:#F97583">do</span></span> <span data-line=""><span style="color:#E1E4E8"> new_user </span><span style="color:#F97583">=</span></span> <span data-line=""><span style="color:#B392F0"> UserDB</span><span style="color:#E1E4E8">.</span><span style="color:#B392F0">add_user</span><span style="color:#E1E4E8">(%{</span></span> <span data-line=""><span style="color:#79B8FF"> first_name:</span><span style="color:#E1E4E8"> request.first_name,</span></span> <span data-line=""><span style="color:#79B8FF"> last_name:</span><span style="color:#E1E4E8"> request.last_name,</span></span> <span data-line=""><span style="color:#79B8FF"> age:</span><span style="color:#E1E4E8"> request.age</span></span> <span data-line=""><span style="color:#E1E4E8"> })</span></span> <span data-line=""> </span> <span data-line=""><span style="color:#B392F0"> SampleApp</span><span style="color:#E1E4E8">.</span><span style="color:#B392F0">UserReply</span><span style="color:#E1E4E8">.</span><span style="color:#B392F0">new</span><span style="color:#E1E4E8">(new_user)</span></span> <span data-line=""><span style="color:#F97583"> end</span></span> <span data-line=""> </span> <span data-line=""><span style="color:#F97583"> def</span><span style="color:#B392F0"> get</span><span style="color:#E1E4E8">(request, </span><span style="color:#6A737D">_stream</span><span style="color:#E1E4E8">) </span><span style="color:#F97583">do</span></span> <span data-line=""><span style="color:#E1E4E8"> user </span><span style="color:#F97583">=</span><span style="color:#B392F0"> UserDB</span><span style="color:#E1E4E8">.</span><span style="color:#B392F0">get_user</span><span style="color:#E1E4E8">(request.id)</span></span> <span data-line=""> </span> <span data-line=""><span style="color:#F97583"> if</span><span style="color:#E1E4E8"> user </span><span style="color:#F97583">==</span><span style="color:#79B8FF"> nil</span><span style="color:#F97583"> do</span></span> <span data-line=""><span style="color:#F97583"> raise</span><span style="color:#B392F0"> GRPC</span><span style="color:#E1E4E8">.</span><span style="color:#B392F0">RPCError</span><span style="color:#E1E4E8">, </span><span style="color:#79B8FF">status:</span><span style="color:#79B8FF"> :not_found</span></span> <span data-line=""><span style="color:#F97583"> else</span></span> <span data-line=""><span style="color:#B392F0"> SampleApp</span><span style="color:#E1E4E8">.</span><span style="color:#B392F0">UserReply</span><span style="color:#E1E4E8">.</span><span style="color:#B392F0">new</span><span style="color:#E1E4E8">(user)</span></span> <span data-line=""><span style="color:#F97583"> end</span></span> <span data-line=""><span style="color:#F97583"> end</span></span> <span data-line=""><span style="color:#F97583">end</span></span></code></div></pre></div></figure> <p>Our file defines two modules. The <code>SampleApp.Endpoint</code> module defines the gRPC server and provides the handler module to service requests. The <code>SampleApp.User.Server</code> module contains the actual implementations of the two RPC calls that we defined. You&#x27;ll notice that for each of the handlers, we provide the correct return type (as defined in our Protocol Buffer file). When we encounter an error (in this case, looking up a user that doesn&#x27;t exist), we raise a <code>GRPC.RPCError</code> with the appropriate status code.</p> <p>All that is left now is to start up our Agent and our gRPC server, and we&#x27;re good to go. Open up <code>lib/sample_app/application.ex</code> and ensure that your process <code>children</code> list looks like this:</p> <figure data-rehype-pretty-code-figure=""><div class="relative my-8 group-[.is-breakout]:md:my-10 group-[.is-breakout]:md:-mx-12" data-rehype-pretty-code-fragment="true"><div class="group-[.code-header-hidden]:hidden absolute top-0 inset-x-0 bg-gray-800 text-white rounded-t-lg overflow-hidden"><div class="flex items-center justify-between pr-4 font-mono text-sm"><div class="h-10"><div class="group-[.is-code-tabs]:hidden flex items-center h-10 px-4 sm:px-6 bg-gray-900">Elixir</div></div><div class="flex items-center"><button class="group/svg group-[.is-code-tabs]:invisible sm:group-[.is-code-tabs]:visible w-6 h-6 pr-1 -mr-1 flex justify-end items-center hover:cursor-pointer"><svg width="13" height="15" viewBox="0 0 13 15" fill="none" xmlns="http://www.w3.org/2000/svg"><path class="transition group-hover/svg:fill-white" d="M8.75 13H4.15625C3.30859 13 2.625 12.3164 2.625 11.4688V3.375H0.65625C0.273438 3.375 0 3.67578 0 4.03125V14.0938C0 14.4766 0.273438 14.75 0.65625 14.75H8.09375C8.44922 14.75 8.75 14.4766 8.75 14.0938V13ZM8.75 3.59375V0.75H4.15625C3.77344 0.75 3.5 1.05078 3.5 1.40625V11.4688C3.5 11.8516 3.77344 12.125 4.15625 12.125H11.5938C11.9492 12.125 12.25 11.8516 12.25 11.4688V4.25H9.40625C9.02344 4.25 8.75 3.97656 8.75 3.59375ZM12.0312 2.74609L10.2539 0.96875C10.1172 0.832031 9.95312 0.75 9.78906 0.75H9.625V3.375H12.25V3.21094C12.25 3.04688 12.168 2.88281 12.0312 2.74609Z" fill="#BFC6D6"></path></svg></button></div></div></div><pre tabindex="0" data-language="elixir" data-theme="github-dark" data-raw="children = [ UserDB, {GRPC.Server.Supervisor, {SampleApp.Endpoint, 50051}} ] " class="z-0 !m-0 !pl-4 dark:bg-gray-100"><div class="sm:px-2 pt-10 group-[.code-header-hidden]:pt-0"><code data-language="elixir" data-theme="github-dark" style="display:grid"><span data-line=""><span style="color:#E1E4E8">children </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> [</span></span> <span data-line=""><span style="color:#B392F0"> UserDB</span><span style="color:#E1E4E8">,</span></span> <span data-line=""><span style="color:#E1E4E8"> {</span><span style="color:#B392F0">GRPC</span><span style="color:#E1E4E8">.</span><span style="color:#B392F0">Server</span><span style="color:#E1E4E8">.</span><span style="color:#B392F0">Supervisor</span><span style="color:#E1E4E8">, {</span><span style="color:#B392F0">SampleApp</span><span style="color:#E1E4E8">.</span><span style="color:#B392F0">Endpoint</span><span style="color:#E1E4E8">, </span><span style="color:#79B8FF">50051</span><span style="color:#E1E4E8">}}</span></span> <span data-line=""><span style="color:#E1E4E8">]</span></span></code></div></pre></div></figure> <p>With that in place, you should be able to run <code>mix grpc.server</code> from the terminal to start your gRPC server. In another terminal session (and from within the project directory), you should be able to use <code>grpcurl</code> commands to interact with your application:</p> <figure data-rehype-pretty-code-figure=""><div class="relative my-8 group-[.is-breakout]:md:my-10 group-[.is-breakout]:md:-mx-12" data-rehype-pretty-code-fragment="true"><div class="group-[.code-header-hidden]:hidden absolute top-0 inset-x-0 bg-gray-800 text-white rounded-t-lg overflow-hidden"><div class="flex items-center justify-between pr-4 font-mono text-sm"><div class="h-10"><div class="group-[.is-code-tabs]:hidden flex items-center h-10 px-4 sm:px-6 bg-gray-900">Shell</div></div><div class="flex items-center"><button class="group/svg group-[.is-code-tabs]:invisible sm:group-[.is-code-tabs]:visible w-6 h-6 pr-1 -mr-1 flex justify-end items-center hover:cursor-pointer"><svg width="13" height="15" viewBox="0 0 13 15" fill="none" xmlns="http://www.w3.org/2000/svg"><path class="transition group-hover/svg:fill-white" d="M8.75 13H4.15625C3.30859 13 2.625 12.3164 2.625 11.4688V3.375H0.65625C0.273438 3.375 0 3.67578 0 4.03125V14.0938C0 14.4766 0.273438 14.75 0.65625 14.75H8.09375C8.44922 14.75 8.75 14.4766 8.75 14.0938V13ZM8.75 3.59375V0.75H4.15625C3.77344 0.75 3.5 1.05078 3.5 1.40625V11.4688C3.5 11.8516 3.77344 12.125 4.15625 12.125H11.5938C11.9492 12.125 12.25 11.8516 12.25 11.4688V4.25H9.40625C9.02344 4.25 8.75 3.97656 8.75 3.59375ZM12.0312 2.74609L10.2539 0.96875C10.1172 0.832031 9.95312 0.75 9.78906 0.75H9.625V3.375H12.25V3.21094C12.25 3.04688 12.168 2.88281 12.0312 2.74609Z" fill="#BFC6D6"></path></svg></button></div></div></div><pre tabindex="0" data-language="shell" data-theme="github-dark" data-raw="$ grpcurl -plaintext -proto sample_app.proto -d &#x27;{&quot;first_name&quot;: &quot;Bob&quot;, &quot;last_name&quot;: &quot;Smith&quot;, &quot;age&quot;: 40}&#x27; localhost:50051 sample_app.User.Create { &quot;id&quot;: 1, &quot;firstName&quot;: &quot;Bob&quot;, &quot;lastName&quot;: &quot;Smith&quot;, &quot;age&quot;: 40 } $ grpcurl -plaintext -proto sample_app.proto -d &#x27;{&quot;id&quot;: 1}&#x27; localhost:50051 sample_app.User.Get { &quot;firstName&quot;: &quot;Bob&quot;, &quot;lastName&quot;: &quot;Smith&quot;, &quot;age&quot;: 40 } $ grpcurl -plaintext -proto sample_app.proto -d &#x27;{&quot;id&quot;: 2}&#x27; localhost:50051 sample_app.User.Get ERROR: Code: NotFound Message: Some requested entity (e.g., file or directory) was not found " class="z-0 !m-0 !pl-4 dark:bg-gray-100"><div class="sm:px-2 pt-10 group-[.code-header-hidden]:pt-0"><code data-language="shell" data-theme="github-dark" style="display:grid"><span data-line=""><span style="color:#B392F0">$</span><span style="color:#9ECBFF"> grpcurl</span><span style="color:#79B8FF"> -plaintext</span><span style="color:#79B8FF"> -proto</span><span style="color:#9ECBFF"> sample_app.proto</span><span style="color:#79B8FF"> -d</span><span style="color:#9ECBFF"> &#x27;{&quot;first_name&quot;: &quot;Bob&quot;, &quot;last_name&quot;: &quot;Smith&quot;, &quot;age&quot;: 40}&#x27;</span><span style="color:#9ECBFF"> localhost:50051</span><span style="color:#9ECBFF"> sample_app.User.Create</span></span> <span data-line=""><span style="color:#E1E4E8">{</span></span> <span data-line=""><span style="color:#B392F0"> &quot;id&quot;</span><span style="color:#79B8FF">:</span><span style="color:#9ECBFF"> 1,</span></span> <span data-line=""><span style="color:#B392F0"> &quot;firstName&quot;</span><span style="color:#79B8FF">:</span><span style="color:#9ECBFF"> &quot;Bob&quot;,</span></span> <span data-line=""><span style="color:#B392F0"> &quot;lastName&quot;</span><span style="color:#79B8FF">:</span><span style="color:#9ECBFF"> &quot;Smith&quot;,</span></span> <span data-line=""><span style="color:#B392F0"> &quot;age&quot;</span><span style="color:#79B8FF">:</span><span style="color:#79B8FF"> 40</span></span> <span data-line=""><span style="color:#E1E4E8">}</span></span> <span data-line=""> </span> <span data-line=""><span style="color:#B392F0">$</span><span style="color:#9ECBFF"> grpcurl</span><span style="color:#79B8FF"> -plaintext</span><span style="color:#79B8FF"> -proto</span><span style="color:#9ECBFF"> sample_app.proto</span><span style="color:#79B8FF"> -d</span><span style="color:#9ECBFF"> &#x27;{&quot;id&quot;: 1}&#x27;</span><span style="color:#9ECBFF"> localhost:50051</span><span style="color:#9ECBFF"> sample_app.User.Get</span></span> <span data-line=""><span style="color:#E1E4E8">{</span></span> <span data-line=""><span style="color:#B392F0"> &quot;firstName&quot;</span><span style="color:#79B8FF">:</span><span style="color:#9ECBFF"> &quot;Bob&quot;,</span></span> <span data-line=""><span style="color:#B392F0"> &quot;lastName&quot;</span><span style="color:#79B8FF">:</span><span style="color:#9ECBFF"> &quot;Smith&quot;,</span></span> <span data-line=""><span style="color:#B392F0"> &quot;age&quot;</span><span style="color:#79B8FF">:</span><span style="color:#79B8FF"> 40</span></span> <span data-line=""><span style="color:#E1E4E8">}</span></span> <span data-line=""> </span> <span data-line=""><span style="color:#B392F0">$</span><span style="color:#9ECBFF"> grpcurl</span><span style="color:#79B8FF"> -plaintext</span><span style="color:#79B8FF"> -proto</span><span style="color:#9ECBFF"> sample_app.proto</span><span style="color:#79B8FF"> -d</span><span style="color:#9ECBFF"> &#x27;{&quot;id&quot;: 2}&#x27;</span><span style="color:#9ECBFF"> localhost:50051</span><span style="color:#9ECBFF"> sample_app.User.Get</span></span> <span data-line=""><span style="color:#B392F0">ERROR:</span></span> <span data-line=""><span style="color:#B392F0"> Code:</span><span style="color:#9ECBFF"> NotFound</span></span> <span data-line=""><span style="color:#B392F0"> Message:</span><span style="color:#9ECBFF"> Some</span><span style="color:#9ECBFF"> requested</span><span style="color:#9ECBFF"> entity</span><span style="color:#E1E4E8"> (e.g., </span><span style="color:#9ECBFF">file</span><span style="color:#9ECBFF"> or</span><span style="color:#9ECBFF"> directory</span><span style="color:#E1E4E8">) was not found</span></span></code></div></pre></div></figure> <h2 id="conclusion">Conclusion</h2> <p>Thanks for sticking with me to the end. Hopefully, you learned a thing or two about gRPC and how to go about using it within an Elixir application. If you would like to learn more about gRPC or any of the tools that I mentioned, I suggest going through the following resources:</p> <ul> <li><a href="https://github.com/elixir-grpc/grpc">elixir-grpc</a></li> <li><a href="https://github.com/tony612/protobuf-elixir">protobuf-elixir</a></li> <li><a href="https://github.com/akoutmos/sample_app">Our sample app</a></li> </ul> <p><strong>P.S. If you&#x27;d like to read Elixir Alchemy posts as soon as they get off the press, <a href="/elixir-alchemy">subscribe to our Elixir Alchemy newsletter and never miss a single post</a>!</strong></p></div></div></article><section class="c-container || content-visibility-auto" id="authors"><div class="max-w-prose mx-auto py-12 space-y-12 border-b border-gray-200"><section class="flex max-w-prose mx-auto space-x-4 sm:space-x-8"><figure class="relative flex-shrink-0 w-14 h-14"><img alt="Alex Koutmos" loading="lazy" decoding="async" data-nimg="fill" class="object-cover rounded-full" style="position:absolute;height:100%;width:100%;left:0;top:0;right:0;bottom:0;color:transparent" sizes="56px" srcSet="/_next/image?url=%2Fimages%2Fauthors%2Falex.jpg&amp;w=16&amp;q=75 16w, /_next/image?url=%2Fimages%2Fauthors%2Falex.jpg&amp;w=32&amp;q=75 32w, /_next/image?url=%2Fimages%2Fauthors%2Falex.jpg&amp;w=48&amp;q=75 48w, /_next/image?url=%2Fimages%2Fauthors%2Falex.jpg&amp;w=64&amp;q=75 64w, /_next/image?url=%2Fimages%2Fauthors%2Falex.jpg&amp;w=96&amp;q=75 96w, /_next/image?url=%2Fimages%2Fauthors%2Falex.jpg&amp;w=128&amp;q=75 128w, /_next/image?url=%2Fimages%2Fauthors%2Falex.jpg&amp;w=256&amp;q=75 256w, /_next/image?url=%2Fimages%2Fauthors%2Falex.jpg&amp;w=384&amp;q=75 384w, /_next/image?url=%2Fimages%2Fauthors%2Falex.jpg&amp;w=640&amp;q=75 640w, /_next/image?url=%2Fimages%2Fauthors%2Falex.jpg&amp;w=750&amp;q=75 750w, /_next/image?url=%2Fimages%2Fauthors%2Falex.jpg&amp;w=828&amp;q=75 828w, /_next/image?url=%2Fimages%2Fauthors%2Falex.jpg&amp;w=1080&amp;q=75 1080w, /_next/image?url=%2Fimages%2Fauthors%2Falex.jpg&amp;w=1200&amp;q=75 1200w, /_next/image?url=%2Fimages%2Fauthors%2Falex.jpg&amp;w=1920&amp;q=75 1920w, /_next/image?url=%2Fimages%2Fauthors%2Falex.jpg&amp;w=2048&amp;q=75 2048w, /_next/image?url=%2Fimages%2Fauthors%2Falex.jpg&amp;w=3840&amp;q=75 3840w" src="/_next/image?url=%2Fimages%2Fauthors%2Falex.jpg&amp;w=3840&amp;q=75"/></figure><div class="space-y-2"><h1 class="c_h-heading c_h-heading--xl leading-tight">Alex Koutmos</h1><div class="space-y-5"><p class="text-gray-700">Guest author Alex Koutmos is a Senior Software Engineer who writes backends in Elixir, frontends in VueJS and deploys his apps using Kubernetes. When he is not programming or blogging he is wrenching on his 1976 Datsun 280z.</p><span class="flex items-center space-x-2 c-link leading-none"><span class="no-underline"></span><a href="/authors/alex-koutmos.html">All articles by <!-- -->Alex Koutmos</a></span></div></div></section></div><div class="max-w-prose mx-auto flex justify-between items-center pt-12 pb-12 sm:pb-20"><div class="flex items-center space-x-2"><svg width="32" height="24" viewBox="0 0 32 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M26.6928 5.65956C24.473 5.65956 22.8803 7.6174 21.2452 9.80278C20.224 5.16745 19.0942 0.537415 15.4987 0.537415C11.9031 0.537415 10.7734 5.16745 9.76008 9.79749C8.12501 7.61476 6.53228 5.65427 4.30986 5.65427C2.89439 5.65956 0.5 6.62261 0.5 10.2261L0.515874 16.7822C0.515874 20.3196 2.40493 21.1742 3.5373 21.4811C5.48986 21.989 10.194 22.46 15.4881 22.46C20.7822 22.46 25.489 21.989 27.4415 21.4811C28.5739 21.1847 30.463 20.3196 30.463 16.7822L30.4815 10.2261C30.4815 6.61996 28.1003 5.65956 26.6716 5.65956M15.4987 2.57992C17.655 2.57992 18.4751 6.83162 19.6181 11.93C18.364 13.4724 17.0332 14.7212 15.4987 14.7212C13.9642 14.7212 12.636 13.4724 11.3793 11.93C12.4508 7.15176 13.2763 2.57992 15.4987 2.57992ZM2.55838 16.7822L2.54251 10.2261C2.54251 7.94812 3.77807 7.70207 4.30721 7.70207C5.87614 7.70207 7.55882 10.3187 9.16743 12.4194C8.02976 17.1817 6.8286 20.092 3.99237 19.4888C3.25421 19.2877 2.55838 18.8195 2.55838 16.7822ZM8.11443 20.0973C9.36851 18.7321 10.1702 16.605 10.7813 14.3508C12.1042 15.7239 13.6096 16.7637 15.4881 16.7637C17.3666 16.7637 18.8826 15.7239 20.1975 14.3508C20.806 16.6023 21.605 18.7321 22.8618 20.0973C20.6684 20.2984 18.0254 20.4175 15.4881 20.4175C12.9508 20.4175 10.3077 20.2984 8.11443 20.0973ZM28.439 16.7822C28.439 18.8195 27.7431 19.2877 27.005 19.4888C24.1661 20.0894 22.9676 17.1844 21.8326 12.4194C23.4385 10.3266 25.1239 7.70207 26.6928 7.70207C27.2219 7.70207 28.4575 7.94812 28.4575 10.2261L28.439 16.7822Z" fill="#415377"></path></svg><p class="text-lg font-semibold leading-none">Become our next author!</p></div><a class="c-button c-button--white c-button--xs space-x-1.5 text-sm" href="/write-for-us.html"><span>Find out more</span></a></div></section><section class="relative flex px-4 sm:px-0 bg-gray-100 || content-visibility-auto"><div class="absolute h-1/3 sm:h-1/3 inset-x-0 inset-y-0 bg-white"></div><div class="z-10 w-full max-w-4xl sm:px-8 lg:px-0 mx-auto"><aside class="sm:flex space-y-6 sm:space-y-0 mx-auto px-4 sm:px-8 md:px-16 py-6 sm:py-8 md:py-12 bg-purple-700 text-white rounded-lg"><div class="sm:pr-16"><div class="w-28 sm:w-32"><img alt="Newsletter Logo" loading="lazy" width="206" height="106" decoding="async" data-nimg="1" style="color:transparent" src="/images/newsletter/logo-elixir.svg"/></div></div><div class="space-y-8"><h1 class="c_h-heading c_h-heading--2xl sm:c_h-heading--3xl">Subscribe to our Elixir Alchemy email series and receive deep insights about Elixir, Phoenix and other developments.</h1><form name="mc-embedded-subscribe-form" action="https://appsignal.us3.list-manage.com/subscribe/post?u=6a5bb0e75a9d07c60a71c7a9f&amp;id=de0b0bb410" method="post"><div class="space-y-3 lg:space-y-0 lg:flex lg:space-x-3"><div class="space-y-3 md:space-y-0 pb-1 md:pb-0 md:flex md:space-x-2 w-full"><div class="w-full flex-grow"><input placeholder="Email" required="" type="email" class="c-form__input px-3 py-2" name="EMAIL"/></div><div class="w-full flex-grow"><input placeholder="First name" required="" type="text" class="c-form__input px-3 py-2" name="NAME"/></div></div><button class="c-button w-full lg:w-auto flex-none" type="submit" name="subscribe"><span class="text-center mx-auto">Subscribe</span></button></div><div aria-hidden="true" style="position:absolute;left:-5000px"><input tabindex="-1" type="text" name="b_6a5bb0e75a9d07c60a71c7a9f_de0b0bb410"/></div></form></div></aside></div></section><div class="pb-12 sm:pb-24 bg-gray-100"></div><section class="bg-gradient-to-b from-gray-900 to-blue-800 || content-visibility-auto"><div class="c-container max-w-7xl text-center pt-20 sm:pt-24 overflow-hidden"><div class="max-w-3xl mx-auto space-y-4 sm:space-y-5"><h1 class="c_h-heading c_h-heading--4xl sm:c_h-heading--5xl text-white">AppSignal monitors your apps</h1><p class="text-lg lg:text-xl text-gray-200">AppSignal provides insights for Ruby, Rails, Elixir, Phoenix, Node.js, Express and many other frameworks and libraries. We are located in beautiful Amsterdam. We love<!-- --> <a href="https://www.appsignal.com/waffles" target="_blank" rel="noopener noreferrer">stroopwafels</a>. If you do too,<!-- --> <a href="mailto:contact@appsignal.com">let us know</a>. We might send you some!</p></div><a href="https://www.appsignal.com" class="mt-10 c-button c-button--md c-button--green">Discover AppSignal</a><div class="relative mt-16"><div class="max-h-40 max-w-md mx-auto || sm:hidden"><img alt="AppSignal monitors your apps" loading="lazy" width="552" height="320" decoding="async" data-nimg="1" style="color:transparent" srcSet="/_next/image?url=%2Fimages%2Fgeneral%2Fcall-to-action-small.png&amp;w=640&amp;q=75 1x, /_next/image?url=%2Fimages%2Fgeneral%2Fcall-to-action-small.png&amp;w=1200&amp;q=75 2x" src="/_next/image?url=%2Fimages%2Fgeneral%2Fcall-to-action-small.png&amp;w=1200&amp;q=75"/></div><div class="max-h-64 md:max-h-80 lg:max-h-100 lg:mb-4 || hidden sm:block"><img alt="AppSignal monitors your apps" loading="lazy" width="1280" height="720" decoding="async" data-nimg="1" style="color:transparent" srcSet="/_next/image?url=%2Fimages%2Fgeneral%2Fcall-to-action.png&amp;w=1920&amp;q=75 1x, /_next/image?url=%2Fimages%2Fgeneral%2Fcall-to-action.png&amp;w=3840&amp;q=75 2x" src="/_next/image?url=%2Fimages%2Fgeneral%2Fcall-to-action.png&amp;w=3840&amp;q=75"/></div></div></div></section></main><footer class="relative px-4 py-16 sm:pt-20 xl:pb-28 dark text-gray-300 bg-gray-900"><div class="c-container"><div class="grid grid-cols-1 sm:grid-cols-2 xl:grid-cols-4 gap-x-12 gap-y-10"><div class="col-span-1"><h2 class="text-white uppercase tracking-wider font-medium border-b border-gray-700 pb-3 mb-4">Features</h2><ul class="space-y-2"><li><div class="flex items-center space-x-3"><span class="h-4 w-4 flex justify-center items-center"><svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="bug" class="svg-inline--fa fa-bug " role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M256 0c53 0 96 43 96 96v3.6c0 15.7-12.7 28.4-28.4 28.4H188.4c-15.7 0-28.4-12.7-28.4-28.4V96c0-53 43-96 96-96zM41.4 105.4c12.5-12.5 32.8-12.5 45.3 0l64 64c.7 .7 1.3 1.4 1.9 2.1c14.2-7.3 30.4-11.4 47.5-11.4H312c17.1 0 33.2 4.1 47.5 11.4c.6-.7 1.2-1.4 1.9-2.1l64-64c12.5-12.5 32.8-12.5 45.3 0s12.5 32.8 0 45.3l-64 64c-.7 .7-1.4 1.3-2.1 1.9c6.2 12 10.1 25.3 11.1 39.5H480c17.7 0 32 14.3 32 32s-14.3 32-32 32H416c0 24.6-5.5 47.8-15.4 68.6c2.2 1.3 4.2 2.9 6 4.8l64 64c12.5 12.5 12.5 32.8 0 45.3s-32.8 12.5-45.3 0l-63.1-63.1c-24.5 21.8-55.8 36.2-90.3 39.6V240c0-8.8-7.2-16-16-16s-16 7.2-16 16V479.2c-34.5-3.4-65.8-17.8-90.3-39.6L86.6 502.6c-12.5 12.5-32.8 12.5-45.3 0s-12.5-32.8 0-45.3l64-64c1.9-1.9 3.9-3.4 6-4.8C101.5 367.8 96 344.6 96 320H32c-17.7 0-32-14.3-32-32s14.3-32 32-32H96.3c1.1-14.1 5-27.5 11.1-39.5c-.7-.6-1.4-1.2-2.1-1.9l-64-64c-12.5-12.5-12.5-32.8 0-45.3z"></path></svg></span><div><a href="https://www.appsignal.com/tour/errors/" class="no-underline text-white undefined sm:text-lg hover:underline">Error tracking</a></div></div></li><li><div class="flex items-center space-x-3"><span class="h-4 w-4 flex justify-center items-center"><svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="rabbit-running" class="svg-inline--fa fa-rabbit-running " role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512"><path fill="currentColor" d="M460.7 39.3l-2.2-2.1c-4.7-4.6-11.5-6.4-17.9-4.7s-11.4 6.6-13.1 13l-.8 2.9c-3.3 11.8-5.1 23.7-5.5 35.6c24.3 20.6 42.9 47.8 53.3 78.8c-8.2-1.9-16.5-2.9-25-2.9c-.6 0-1.1 0-1.7 0c-18.4-44-56.5-77.4-103.6-89.3l-3.4-.9c-6.2-1.6-12.9-.6-18.5 2.6c-10.8 6.2-15.6 19.5-11.2 31.1c14.9 39.5 44.3 71.4 81.4 89.7c-3.7 6.6-6.4 14-7.6 21.8L279.7 154.9C248.8 137.3 213.8 128 178.2 128c-32.3 0-62.2 16.8-78.9 44.4C89 159.9 73.4 152 56 152c-30.9 0-56 25.1-56 56s25.1 56 56 56c11.3 0 21.8-3.3 30.6-9.1c4.9 17.2 14.5 33.1 28.4 45.9L257.2 431.2C269 442 284.4 448 300.4 448H416c17.7 0 32-14.3 32-32s-14.3-32-32-32H352 335.2 320V340.5c0-42.9-28.4-80.5-69.6-92.3l-30.8-8.8c-8.5-2.4-13.4-11.3-11-19.8s11.3-13.4 19.8-11l30.8 8.8c55 15.7 92.8 65.9 92.8 123.1v15.7l56-32.4 6.4-3.7H515.7c33.3 0 60.3-27 60.3-60.3c0-18.1-8.2-35.3-22.2-46.7l-34.6-28.2c-4.6-3.7-9.4-7.1-14.5-10c8.4-49.3-7.8-100-43.9-135.5zm-318.5 382c-14.7 9.8-18.7 29.7-8.9 44.4s29.7 18.7 44.4 8.9l46-30.7-48.6-44.5-32.9 22zM480 240a16 16 0 1 1 32 0 16 16 0 1 1 -32 0z"></path></svg></span><div><a href="https://www.appsignal.com/tour/performance" class="no-underline text-white undefined sm:text-lg hover:underline">Performance monitoring</a></div></div></li><li><div class="flex items-center space-x-3"><span class="h-4 w-4 flex justify-center items-center"><svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="server" class="svg-inline--fa fa-server " role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M64 32C28.7 32 0 60.7 0 96v64c0 35.3 28.7 64 64 64H448c35.3 0 64-28.7 64-64V96c0-35.3-28.7-64-64-64H64zm280 72a24 24 0 1 1 0 48 24 24 0 1 1 0-48zm48 24a24 24 0 1 1 48 0 24 24 0 1 1 -48 0zM64 288c-35.3 0-64 28.7-64 64v64c0 35.3 28.7 64 64 64H448c35.3 0 64-28.7 64-64V352c0-35.3-28.7-64-64-64H64zm280 72a24 24 0 1 1 0 48 24 24 0 1 1 0-48zm56 24a24 24 0 1 1 48 0 24 24 0 1 1 -48 0z"></path></svg></span><div><a href="https://www.appsignal.com/tour/hosts" class="no-underline text-white undefined sm:text-lg hover:underline">Host monitoring</a></div></div></li><li><div class="flex items-center space-x-3"><span class="h-4 w-4 flex justify-center items-center"><svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="siren-on" class="svg-inline--fa fa-siren-on " role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 512"><path fill="currentColor" d="M69.3 36l48 32c11 7.4 14 22.3 6.7 33.3s-22.3 14-33.3 6.7l-48-32c-11-7.4-14-22.3-6.7-33.3s22.3-14 33.3-6.7zM597.3 76l-48 32c-11 7.4-25.9 4.4-33.3-6.7s-4.4-25.9 6.7-33.3l48-32c11-7.4 25.9-4.4 33.3 6.7s4.4 25.9-6.7 33.3zM24 192H88c13.3 0 24 10.7 24 24s-10.7 24-24 24H24c-13.3 0-24-10.7-24-24s10.7-24 24-24zm528 0h64c13.3 0 24 10.7 24 24s-10.7 24-24 24H552c-13.3 0-24-10.7-24-24s10.7-24 24-24zM144 352l25-199.9c4-32 31.2-56.1 63.5-56.1h175c32.3 0 59.5 24 63.5 56.1L496 352H234.1l21.8-174c1.1-8.8-5.1-16.8-13.9-17.9s-16.8 5.1-17.9 13.9L201.9 352H144zM96 416c0-17.7 14.3-32 32-32H512c17.7 0 32 14.3 32 32v32c0 17.7-14.3 32-32 32H128c-17.7 0-32-14.3-32-32V416z"></path></svg></span><div><a href="https://www.appsignal.com/tour/anomaly-detection" class="no-underline text-white undefined sm:text-lg hover:underline">Anomaly detection</a></div></div></li><li><div class="flex items-center space-x-3"><span class="h-4 w-4 flex justify-center items-center"><svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="traffic-light" class="svg-inline--fa fa-traffic-light " role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 320 512"><path fill="currentColor" d="M64 0C28.7 0 0 28.7 0 64V352c0 88.4 71.6 160 160 160s160-71.6 160-160V64c0-35.3-28.7-64-64-64H64zm96 416a48 48 0 1 1 0-96 48 48 0 1 1 0 96zm48-176a48 48 0 1 1 -96 0 48 48 0 1 1 96 0zm-48-80a48 48 0 1 1 0-96 48 48 0 1 1 0 96z"></path></svg></span><div><a href="https://www.appsignal.com/tour/uptime-monitoring" class="no-underline text-white undefined sm:text-lg hover:underline">Uptime monitoring</a></div></div></li><li><div class="flex items-center space-x-3"><span class="h-4 w-4 flex justify-center items-center"><svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="gauge-high" class="svg-inline--fa fa-gauge-high " role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M0 256a256 256 0 1 1 512 0A256 256 0 1 1 0 256zM288 96a32 32 0 1 0 -64 0 32 32 0 1 0 64 0zM256 416c35.3 0 64-28.7 64-64c0-17.4-6.9-33.1-18.1-44.6L366 161.7c5.3-12.1-.2-26.3-12.3-31.6s-26.3 .2-31.6 12.3L257.9 288c-.6 0-1.3 0-1.9 0c-35.3 0-64 28.7-64 64s28.7 64 64 64zM176 144a32 32 0 1 0 -64 0 32 32 0 1 0 64 0zM96 288a32 32 0 1 0 0-64 32 32 0 1 0 0 64zm352-32a32 32 0 1 0 -64 0 32 32 0 1 0 64 0z"></path></svg></span><div><a href="https://www.appsignal.com/tour/metrics" class="no-underline text-white undefined sm:text-lg hover:underline">Metric dashboards</a></div></div></li><li><div class="flex items-center space-x-3"><span class="h-4 w-4 flex justify-center items-center"><svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="users" class="svg-inline--fa fa-users " role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 512"><path fill="currentColor" d="M144 0a80 80 0 1 1 0 160A80 80 0 1 1 144 0zM512 0a80 80 0 1 1 0 160A80 80 0 1 1 512 0zM0 298.7C0 239.8 47.8 192 106.7 192h42.7c15.9 0 31 3.5 44.6 9.7c-1.3 7.2-1.9 14.7-1.9 22.3c0 38.2 16.8 72.5 43.3 96c-.2 0-.4 0-.7 0H21.3C9.6 320 0 310.4 0 298.7zM405.3 320c-.2 0-.4 0-.7 0c26.6-23.5 43.3-57.8 43.3-96c0-7.6-.7-15-1.9-22.3c13.6-6.3 28.7-9.7 44.6-9.7h42.7C592.2 192 640 239.8 640 298.7c0 11.8-9.6 21.3-21.3 21.3H405.3zM224 224a96 96 0 1 1 192 0 96 96 0 1 1 -192 0zM128 485.3C128 411.7 187.7 352 261.3 352H378.7C452.3 352 512 411.7 512 485.3c0 14.7-11.9 26.7-26.7 26.7H154.7c-14.7 0-26.7-11.9-26.7-26.7z"></path></svg></span><div><a href="https://www.appsignal.com/tour/workflow" class="no-underline text-white undefined sm:text-lg hover:underline">Workflow</a></div></div></li><li><div class="flex items-center space-x-3"><span class="h-4 w-4 flex justify-center items-center"><svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="line-columns" class="svg-inline--fa fa-line-columns " role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M224 64c0-17.7-14.3-32-32-32H32C14.3 32 0 46.3 0 64S14.3 96 32 96H192c17.7 0 32-14.3 32-32zm0 128c0-17.7-14.3-32-32-32H32c-17.7 0-32 14.3-32 32s14.3 32 32 32H192c17.7 0 32-14.3 32-32zM0 320c0 17.7 14.3 32 32 32H192c17.7 0 32-14.3 32-32s-14.3-32-32-32H32c-17.7 0-32 14.3-32 32zM224 448c0-17.7-14.3-32-32-32H32c-17.7 0-32 14.3-32 32s14.3 32 32 32H192c17.7 0 32-14.3 32-32zM288 64c0 17.7 14.3 32 32 32H480c17.7 0 32-14.3 32-32s-14.3-32-32-32H320c-17.7 0-32 14.3-32 32zM512 192c0-17.7-14.3-32-32-32H320c-17.7 0-32 14.3-32 32s14.3 32 32 32H480c17.7 0 32-14.3 32-32zM288 320c0 17.7 14.3 32 32 32H480c17.7 0 32-14.3 32-32s-14.3-32-32-32H320c-17.7 0-32 14.3-32 32zM512 448c0-17.7-14.3-32-32-32H320c-17.7 0-32 14.3-32 32s14.3 32 32 32H480c17.7 0 32-14.3 32-32z"></path></svg></span><div><a href="https://www.appsignal.com/tour/log-management" class="no-underline text-white undefined sm:text-lg hover:underline">Log management</a></div></div></li><li><div class="flex items-center space-x-3"><span class="h-4 w-4 flex justify-center items-center"><svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="gauge-high" class="svg-inline--fa fa-gauge-high " role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M0 256a256 256 0 1 1 512 0A256 256 0 1 1 0 256zM288 96a32 32 0 1 0 -64 0 32 32 0 1 0 64 0zM256 416c35.3 0 64-28.7 64-64c0-17.4-6.9-33.1-18.1-44.6L366 161.7c5.3-12.1-.2-26.3-12.3-31.6s-26.3 .2-31.6 12.3L257.9 288c-.6 0-1.3 0-1.9 0c-35.3 0-64 28.7-64 64s28.7 64 64 64zM176 144a32 32 0 1 0 -64 0 32 32 0 1 0 64 0zM96 288a32 32 0 1 0 0-64 32 32 0 1 0 0 64zm352-32a32 32 0 1 0 -64 0 32 32 0 1 0 64 0z"></path></svg></span><div><a href="https://www.appsignal.com/tour/automated-dashboards" class="no-underline text-white undefined sm:text-lg hover:underline">Automated Dashboards</a></div></div></li><li><div class="flex items-center space-x-3"><span class="h-4 w-4 flex justify-center items-center"><svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="square-check" class="svg-inline--fa fa-square-check " role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><path fill="currentColor" d="M64 32C28.7 32 0 60.7 0 96V416c0 35.3 28.7 64 64 64H384c35.3 0 64-28.7 64-64V96c0-35.3-28.7-64-64-64H64zM337 209L209 337c-9.4 9.4-24.6 9.4-33.9 0l-64-64c-9.4-9.4-9.4-24.6 0-33.9s24.6-9.4 33.9 0l47 47L303 175c9.4-9.4 24.6-9.4 33.9 0s9.4 24.6 0 33.9z"></path></svg></span><div><a href="https://www.appsignal.com/tour/check-ins" class="no-underline text-white undefined sm:text-lg hover:underline">Check-ins</a></div></div></li><li><div class="flex items-center space-x-3"><span class="h-4 w-4 flex justify-center items-center"></span><div><a href="https://www.appsignal.com/tour/time-detective" class="no-underline text-white undefined sm:text-lg hover:underline">Time Detective</a></div></div></li></ul></div><div class="col-span-1"><h2 class="text-white uppercase tracking-wider font-medium border-b border-gray-700 pb-3 mb-4">Resources</h2><ul class="space-y-2"><li><div class="flex items-center space-x-3"><div><a href="https://www.appsignal.com/plans" class="no-underline text-white undefined sm:text-lg hover:underline">Plans &amp; pricing</a></div></div></li><li><div class="flex items-center space-x-3"><div><a href="https://docs.appsignal.com/" class="no-underline text-white undefined sm:text-lg hover:underline">Documentation</a></div></div></li><li><div class="flex items-center space-x-3"><div><a class="no-underline text-white undefined sm:text-lg hover:underline" href="/">Blog</a></div></div></li><li><div class="flex items-center space-x-3"><div><a href="https://www.appsignal.com/customers" class="no-underline text-white undefined sm:text-lg hover:underline">Customer Stories</a></div></div></li><li><div class="flex items-center space-x-3"><div><a href="https://www.appsignal.com/alternative/datadog-alternative" class="no-underline text-white undefined sm:text-lg hover:underline">Compare AppSignal to Datadog</a></div></div></li><li><div class="flex items-center space-x-3"><div><a href="https://www.appsignal.com/alternative/new-relic-alternative" class="no-underline text-white undefined sm:text-lg hover:underline">Compare AppSignal to New Relic</a></div></div></li><li><div class="flex items-center space-x-3"><div><a href="https://www.appsignal.com/alternative/sentry-alternative" class="no-underline text-white undefined sm:text-lg hover:underline">Compare AppSignal to Sentry</a></div></div></li><li><div class="flex items-center space-x-3"><div><a href="https://www.appsignal.com/changelog" class="no-underline text-white undefined sm:text-lg hover:underline">Changelog</a></div></div></li><li><div class="flex items-center space-x-3"><div><a href="https://www.appsignal.com/learning-center" class="no-underline text-white undefined sm:text-lg hover:underline">Learning Center</a></div></div></li><li><div class="flex items-center space-x-3"><div><a href="https://www.appsignal.com/why" class="no-underline text-white undefined sm:text-lg hover:underline">Why AppSignal</a></div></div></li></ul></div><div class="col-span-1"><h2 class="text-white uppercase tracking-wider font-medium border-b border-gray-700 pb-3 mb-4">Support</h2><p class="text-sm mb-4">Do you need help, have a feature request or just need someone to rubber duck with? Get in touch with one of our engineers.</p><ul class="space-y-2"><li><div class="flex items-center space-x-3"><div><a href="https://www.appsignal.com/contact" class="no-underline text-white undefined sm:text-lg hover:underline">Contact us</a></div></div></li><li><div class="flex items-center space-x-3"><div><a href="mailto:support@appsignal.com" class="no-underline text-white undefined sm:text-lg hover:underline">Live chat</a></div></div></li><li><div class="flex items-center space-x-3"><div><a href="https://status.appsignal.com/" class="no-underline text-white undefined sm:text-lg hover:underline">Status</a></div></div></li><li><div class="flex items-center space-x-3"><div><a href="https://www.appsignal.com/security" class="no-underline text-white undefined sm:text-lg hover:underline">Security</a></div></div></li></ul></div><div class="col-span-1"><h2 class="text-white uppercase tracking-wider font-medium border-b border-gray-700 pb-3 mb-4">About us</h2><p class="text-sm mb-4"><span>AppSignal is located in the beautiful Netherlands. We love<!-- --> <a href="https://www.appsignal.com/waffles">stroopwafels</a>. If you do too, <a href="mailto:support@appsignal.com">let us know</a>. We might send you some!</span></p><ul class="space-y-2"><li><div class="flex items-center space-x-3"><div><a href="https://www.appsignal.com/about" class="no-underline text-white undefined sm:text-lg hover:underline">About</a></div></div></li><li><div class="flex items-center space-x-3"><div><a href="https://www.appsignal.com/jobs" class="no-underline text-white undefined sm:text-lg hover:underline">Jobs</a></div></div></li><li><div class="flex items-center space-x-3"><div><a class="no-underline text-white undefined sm:text-lg hover:underline" href="/write-for-us">Write for Our Blog</a></div></div></li><li><div class="flex items-center space-x-3"><div><a href="https://www.appsignal.com/diversity" class="no-underline text-white undefined sm:text-lg hover:underline">Diversity</a></div></div></li><li><div class="flex items-center space-x-3"><div><a href="https://www.appsignal.com/open-source" class="no-underline text-white undefined sm:text-lg hover:underline">Open Source</a></div></div></li><li><div class="flex items-center space-x-3"><div><a href="https://twitter.com/appsignal" class="no-underline text-white undefined sm:text-lg hover:underline">Twitter</a></div></div></li></ul></div><div class="col-span-1 sm:col-span-2 xl:col-span-4"><h2 class="text-white uppercase tracking-wider font-medium border-b border-gray-700 pb-3 mb-4">Languages</h2><div class="grid grid-cols-1 sm:grid-cols-3 gap-6 sm:gap-12"><div class="space-y-6 sm:space-y-12 lg:space-y-0 lg:grid lg:grid-cols-2 lg:gap-12"><ul class="space-y-2"><li><div class="flex items-center space-x-2"><span class="h-4 w-4 flex justify-center items-center"><svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="gem" class="svg-inline--fa fa-gem " role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M116.7 33.8c4.5-6.1 11.7-9.8 19.3-9.8H376c7.6 0 14.8 3.6 19.3 9.8l112 152c6.8 9.2 6.1 21.9-1.5 30.4l-232 256c-4.5 5-11 7.9-17.8 7.9s-13.2-2.9-17.8-7.9l-232-256c-7.7-8.5-8.3-21.2-1.5-30.4l112-152zm38.5 39.8c-3.3 2.5-4.2 7-2.1 10.5l57.4 95.6L63.3 192c-4.1 .3-7.3 3.8-7.3 8s3.2 7.6 7.3 8l192 16c.4 0 .9 0 1.3 0l192-16c4.1-.3 7.3-3.8 7.3-8s-3.2-7.6-7.3-8L301.5 179.8l57.4-95.6c2.1-3.5 1.2-8.1-2.1-10.5s-7.9-2-10.7 1L256 172.2 165.9 74.6c-2.8-3-7.4-3.4-10.7-1z"></path></svg></span><div><a href="https://www.appsignal.com/ruby" class="no-underline text-white text-lg sm:text-lg hover:underline">Ruby</a></div></div><p class="mt-1"><span class="sm:block"><a href="https://www.appsignal.com/ruby/active-record-monitoring" class="no-underline hover:underline">Active Record</a><span class="sm:hidden">, </span></span><span class="sm:block"><a href="https://www.appsignal.com/ruby/capistrano-monitoring" class="no-underline hover:underline">Capistrano</a><span class="sm:hidden">, </span></span><span class="sm:block"><a href="https://www.appsignal.com/ruby/delayed-job-monitoring" class="no-underline hover:underline">Delayed::Job</a><span class="sm:hidden">, </span></span><span class="sm:block"><a href="https://www.appsignal.com/ruby/garbage-collection-monitoring" class="no-underline hover:underline">Garbage Collection</a><span class="sm:hidden">, </span></span><span class="sm:block"><a href="https://www.appsignal.com/ruby/global-vm-lock-monitoring" class="no-underline hover:underline">Global VM Lock</a><span class="sm:hidden">, </span></span><span class="sm:block"><a href="https://www.appsignal.com/ruby/grape-monitoring" class="no-underline hover:underline">Grape</a><span class="sm:hidden">, </span></span><span class="sm:block"><a href="https://www.appsignal.com/ruby/graphql-monitoring" class="no-underline hover:underline">GraphQL</a><span class="sm:hidden">, </span></span><span class="sm:block"><a href="https://www.appsignal.com/ruby/hanami-monitoring" class="no-underline hover:underline">Hanami</a><span class="sm:hidden">, </span></span><span class="sm:block"><a href="https://www.appsignal.com/ruby/mongodb-monitoring" class="no-underline hover:underline">MongoDB</a><span class="sm:hidden">, </span></span><span class="sm:block"><a href="https://www.appsignal.com/ruby/padrino-monitoring" class="no-underline hover:underline">Padrino</a><span class="sm:hidden">, </span></span><span class="sm:block"><a href="https://www.appsignal.com/ruby/puma-monitoring" class="no-underline hover:underline">Puma</a><span class="sm:hidden">, </span></span><span class="sm:block"><a href="https://www.appsignal.com/ruby/que-monitoring" class="no-underline hover:underline">Que</a><span class="sm:hidden">, </span></span><span class="sm:block"><a href="https://www.appsignal.com/ruby/rails-monitoring" class="no-underline hover:underline">Ruby on Rails</a><span class="sm:hidden">, </span></span><span class="sm:block"><a href="https://www.appsignal.com/ruby/rake-monitoring" class="no-underline hover:underline">Rake</a><span class="sm:hidden">, </span></span><span class="sm:block"><a href="https://www.appsignal.com/ruby/resque-monitoring" class="no-underline hover:underline">Resque</a><span class="sm:hidden">, </span></span><span class="sm:block"><a href="https://www.appsignal.com/ruby/shoryuken-monitoring" class="no-underline hover:underline">Shoryuken</a><span class="sm:hidden">, </span></span><span class="sm:block"><a href="https://www.appsignal.com/ruby/sidekiq-monitoring" class="no-underline hover:underline">Sidekiq</a><span class="sm:hidden">, </span></span><span class="sm:block"><a href="https://www.appsignal.com/ruby/sinatra-monitoring" class="no-underline hover:underline">Sinatra</a><span class="sm:hidden">, </span></span><span class="sm:block"><a href="https://www.appsignal.com/ruby/solid-queue-monitoring" class="no-underline hover:underline">Solid Queue</a><span class="sm:hidden">, </span></span><span class="sm:block"><a href="https://www.appsignal.com/ruby/view-component-monitoring" class="no-underline hover:underline">ViewComponent</a><span class="sm:hidden">, </span></span><span class="sm:block"><a href="https://www.appsignal.com/ruby/webmachine-monitoring" class="no-underline hover:underline">Webmachine</a><span class="sm:hidden"></span></span></p></li></ul><ul class="space-y-2"><li><div class="flex items-center space-x-2"><span class="h-4 w-4 flex justify-center items-center"><svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="droplet" class="svg-inline--fa fa-droplet " role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 384 512"><path fill="currentColor" d="M192 512C86 512 0 426 0 320C0 228.8 130.2 57.7 166.6 11.7C172.6 4.2 181.5 0 191.1 0h1.8c9.6 0 18.5 4.2 24.5 11.7C253.8 57.7 384 228.8 384 320c0 106-86 192-192 192zM96 336c0-8.8-7.2-16-16-16s-16 7.2-16 16c0 61.9 50.1 112 112 112c8.8 0 16-7.2 16-16s-7.2-16-16-16c-44.2 0-80-35.8-80-80z"></path></svg></span><div><a href="https://www.appsignal.com/elixir" class="no-underline text-white text-lg sm:text-lg hover:underline">Elixir</a></div></div><p class="mt-1"><span class="sm:block"><a href="https://www.appsignal.com/elixir/absinthe-monitoring" class="no-underline hover:underline">Absinthe</a><span class="sm:hidden">, </span></span><span class="sm:block"><a href="https://www.appsignal.com/elixir/ecto-monitoring" class="no-underline hover:underline">Ecto</a><span class="sm:hidden">, </span></span><span class="sm:block"><a href="https://www.appsignal.com/elixir/erlang-monitoring" class="no-underline hover:underline">Erlang</a><span class="sm:hidden">, </span></span><span class="sm:block"><a href="https://www.appsignal.com/elixir/finch-monitoring" class="no-underline hover:underline">Finch</a><span class="sm:hidden">, </span></span><span class="sm:block"><a href="https://www.appsignal.com/elixir/oban-monitoring" class="no-underline hover:underline">Oban</a><span class="sm:hidden">, </span></span><span class="sm:block"><a href="https://www.appsignal.com/elixir/phoenix-monitoring" class="no-underline hover:underline">Phoenix</a><span class="sm:hidden">, </span></span><span class="sm:block"><a href="https://www.appsignal.com/elixir/plug-monitoring" class="no-underline hover:underline">Plug</a><span class="sm:hidden">, </span></span><span class="sm:block"><a href="https://www.appsignal.com/elixir/tesla-monitoring" class="no-underline hover:underline">Tesla</a><span class="sm:hidden"></span></span></p></li></ul></div><div class="space-y-6 sm:space-y-12 lg:space-y-0 lg:grid lg:grid-cols-2 lg:gap-12"><ul class="space-y-2"><li><div class="flex items-center space-x-2"><span class="h-4 w-4 flex justify-center items-center"><svg aria-hidden="true" focusable="false" data-prefix="fab" data-icon="node-js" class="svg-inline--fa fa-node-js " role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><path fill="currentColor" d="M224 508c-6.7 0-13.5-1.8-19.4-5.2l-61.7-36.5c-9.2-5.2-4.7-7-1.7-8 12.3-4.3 14.8-5.2 27.9-12.7 1.4-.8 3.2-.5 4.6.4l47.4 28.1c1.7 1 4.1 1 5.7 0l184.7-106.6c1.7-1 2.8-3 2.8-5V149.3c0-2.1-1.1-4-2.9-5.1L226.8 37.7c-1.7-1-4-1-5.7 0L36.6 144.3c-1.8 1-2.9 3-2.9 5.1v213.1c0 2 1.1 4 2.9 4.9l50.6 29.2c27.5 13.7 44.3-2.4 44.3-18.7V167.5c0-3 2.4-5.3 5.4-5.3h23.4c2.9 0 5.4 2.3 5.4 5.3V378c0 36.6-20 57.6-54.7 57.6-10.7 0-19.1 0-42.5-11.6l-48.4-27.9C8.1 389.2.7 376.3.7 362.4V149.3c0-13.8 7.4-26.8 19.4-33.7L204.6 9c11.7-6.6 27.2-6.6 38.8 0l184.7 106.7c12 6.9 19.4 19.8 19.4 33.7v213.1c0 13.8-7.4 26.7-19.4 33.7L243.4 502.8c-5.9 3.4-12.6 5.2-19.4 5.2zm149.1-210.1c0-39.9-27-50.5-83.7-58-57.4-7.6-63.2-11.5-63.2-24.9 0-11.1 4.9-25.9 47.4-25.9 37.9 0 51.9 8.2 57.7 33.8.5 2.4 2.7 4.2 5.2 4.2h24c1.5 0 2.9-.6 3.9-1.7s1.5-2.6 1.4-4.1c-3.7-44.1-33-64.6-92.2-64.6-52.7 0-84.1 22.2-84.1 59.5 0 40.4 31.3 51.6 81.8 56.6 60.5 5.9 65.2 14.8 65.2 26.7 0 20.6-16.6 29.4-55.5 29.4-48.9 0-59.6-12.3-63.2-36.6-.4-2.6-2.6-4.5-5.3-4.5h-23.9c-3 0-5.3 2.4-5.3 5.3 0 31.1 16.9 68.2 97.8 68.2 58.4-.1 92-23.2 92-63.4z"></path></svg></span><div><a href="https://www.appsignal.com/nodejs" class="no-underline text-white text-lg sm:text-lg hover:underline">Node.js</a></div></div><p class="mt-1"><span class="sm:block"><a href="https://www.appsignal.com/nodejs/amqplib-monitoring" class="no-underline hover:underline">AMQPlib</a><span class="sm:hidden">, </span></span><span class="sm:block"><a href="https://www.appsignal.com/nodejs/apollo-gateway-monitoring" class="no-underline hover:underline">Apollo Gateway</a><span class="sm:hidden">, </span></span><span class="sm:block"><a href="https://www.appsignal.com/nodejs/bullmq-monitoring" class="no-underline hover:underline">BullMQ</a><span class="sm:hidden">, </span></span><span class="sm:block"><a href="https://www.appsignal.com/nodejs/express-monitoring" class="no-underline hover:underline">Express</a><span class="sm:hidden">, </span></span><span class="sm:block"><a href="https://www.appsignal.com/nodejs/fastify-monitoring" class="no-underline hover:underline">Fastify</a><span class="sm:hidden">, </span></span><span class="sm:block"><a href="https://www.appsignal.com/nodejs/graphql-monitoring" class="no-underline hover:underline">GraphQL</a><span class="sm:hidden">, </span></span><span class="sm:block"><a href="https://www.appsignal.com/nodejs/knexjs-monitoring" class="no-underline hover:underline">Knex.js</a><span class="sm:hidden">, </span></span><span class="sm:block"><a href="https://www.appsignal.com/nodejs/koa-monitoring" class="no-underline hover:underline">Koa</a><span class="sm:hidden">, </span></span><span class="sm:block"><a href="https://www.appsignal.com/nodejs/mongodb-monitoring" class="no-underline hover:underline">MongoDB</a><span class="sm:hidden">, </span></span><span class="sm:block"><a href="https://www.appsignal.com/nodejs/mongoose-monitoring" class="no-underline hover:underline">Mongoose</a><span class="sm:hidden">, </span></span><span class="sm:block"><a href="https://www.appsignal.com/nodejs/mysql-monitoring" class="no-underline hover:underline">MySQL</a><span class="sm:hidden">, </span></span><span class="sm:block"><a href="https://www.appsignal.com/nodejs/nestjs-monitoring" class="no-underline hover:underline">NestJS</a><span class="sm:hidden">, </span></span><span class="sm:block"><a href="https://www.appsignal.com/nodejs/nextjs-monitoring" class="no-underline hover:underline">Next.js</a><span class="sm:hidden">, </span></span><span class="sm:block"><a href="https://www.appsignal.com/nodejs/pino-monitoring" class="no-underline hover:underline">Pino</a><span class="sm:hidden">, </span></span><span class="sm:block"><a href="https://www.appsignal.com/nodejs/postgresql-monitoring" class="no-underline hover:underline">PostgreSQL</a><span class="sm:hidden">, </span></span><span class="sm:block"><a href="https://www.appsignal.com/nodejs/prisma-monitoring" class="no-underline hover:underline">Prisma</a><span class="sm:hidden">, </span></span><span class="sm:block"><a href="https://www.appsignal.com/nodejs/redis-monitoring" class="no-underline hover:underline">Redis</a><span class="sm:hidden">, </span></span><span class="sm:block"><a href="https://www.appsignal.com/nodejs/remix-monitoring" class="no-underline hover:underline">Remix</a><span class="sm:hidden">, </span></span><span class="sm:block"><a href="https://www.appsignal.com/nodejs/restify-monitoring" class="no-underline hover:underline">Restify</a><span class="sm:hidden">, </span></span><span class="sm:block"><a href="https://www.appsignal.com/nodejs/winston-monitoring" class="no-underline hover:underline">Winston</a><span class="sm:hidden"></span></span></p></li></ul><ul class="space-y-2"><li><div class="flex items-center space-x-2"><span class="h-4 w-4 flex justify-center items-center"><svg aria-hidden="true" focusable="false" data-prefix="fab" data-icon="square-js" class="svg-inline--fa fa-square-js " role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><path fill="currentColor" d="M448 96c0-35.3-28.7-64-64-64H64C28.7 32 0 60.7 0 96V416c0 35.3 28.7 64 64 64H384c35.3 0 64-28.7 64-64V96zM180.9 444.9c-33.7 0-53.2-17.4-63.2-38.5L152 385.7c6.6 11.7 12.6 21.6 27.1 21.6c13.8 0 22.6-5.4 22.6-26.5V237.7h42.1V381.4c0 43.6-25.6 63.5-62.9 63.5zm85.8-43L301 382.1c9 14.7 20.8 25.6 41.5 25.6c17.4 0 28.6-8.7 28.6-20.8c0-14.4-11.4-19.5-30.7-28l-10.5-4.5c-30.4-12.9-50.5-29.2-50.5-63.5c0-31.6 24.1-55.6 61.6-55.6c26.8 0 46 9.3 59.8 33.7L368 290c-7.2-12.9-15-18-27.1-18c-12.3 0-20.1 7.8-20.1 18c0 12.6 7.8 17.7 25.9 25.6l10.5 4.5c35.8 15.3 55.9 31 55.9 66.2c0 37.8-29.8 58.6-69.7 58.6c-39.1 0-64.4-18.6-76.7-43z"></path></svg></span><div><a href="https://www.appsignal.com/javascript" class="no-underline text-white text-lg sm:text-lg hover:underline">JavaScript</a></div></div><p class="mt-1"><span class="sm:block"><a href="https://www.appsignal.com/javascript/react-monitoring" class="no-underline hover:underline">React</a><span class="sm:hidden">, </span></span><span class="sm:block"><a href="https://www.appsignal.com/javascript/vue-monitoring" class="no-underline hover:underline">Vue</a><span class="sm:hidden">, </span></span><span class="sm:block"><a href="https://www.appsignal.com/javascript/angular-monitoring" class="no-underline hover:underline">Angular</a><span class="sm:hidden">, </span></span><span class="sm:block"><a href="https://www.appsignal.com/javascript/ember-monitoring" class="no-underline hover:underline">Ember</a><span class="sm:hidden">, </span></span><span class="sm:block"><a href="https://www.appsignal.com/javascript/preact-monitoring" class="no-underline hover:underline">Preact</a><span class="sm:hidden">, </span></span><span class="sm:block"><a href="https://www.appsignal.com/javascript/stimulus-monitoring" class="no-underline hover:underline">Stimulus</a><span class="sm:hidden"></span></span></p></li></ul></div><div class="space-y-6 sm:space-y-12 lg:space-y-0 lg:grid lg:grid-cols-2 lg:gap-12"><ul class="space-y-2"><li><div class="flex items-center space-x-2"><span class="h-4 w-4 flex justify-center items-center"><svg aria-hidden="true" focusable="false" data-prefix="fab" data-icon="python" class="svg-inline--fa fa-python " role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><path fill="currentColor" d="M439.8 200.5c-7.7-30.9-22.3-54.2-53.4-54.2h-40.1v47.4c0 36.8-31.2 67.8-66.8 67.8H172.7c-29.2 0-53.4 25-53.4 54.3v101.8c0 29 25.2 46 53.4 54.3 33.8 9.9 66.3 11.7 106.8 0 26.9-7.8 53.4-23.5 53.4-54.3v-40.7H226.2v-13.6h160.2c31.1 0 42.6-21.7 53.4-54.2 11.2-33.5 10.7-65.7 0-108.6zM286.2 404c11.1 0 20.1 9.1 20.1 20.3 0 11.3-9 20.4-20.1 20.4-11 0-20.1-9.2-20.1-20.4.1-11.3 9.1-20.3 20.1-20.3zM167.8 248.1h106.8c29.7 0 53.4-24.5 53.4-54.3V91.9c0-29-24.4-50.7-53.4-55.6-35.8-5.9-74.7-5.6-106.8.1-45.2 8-53.4 24.7-53.4 55.6v40.7h106.9v13.6h-147c-31.1 0-58.3 18.7-66.8 54.2-9.8 40.7-10.2 66.1 0 108.6 7.6 31.6 25.7 54.2 56.8 54.2H101v-48.8c0-35.3 30.5-66.4 66.8-66.4zm-6.7-142.6c-11.1 0-20.1-9.1-20.1-20.3.1-11.3 9-20.4 20.1-20.4 11 0 20.1 9.2 20.1 20.4s-9 20.3-20.1 20.3z"></path></svg></span><div><a href="https://www.appsignal.com/python" class="no-underline text-white text-lg sm:text-lg hover:underline">Python</a></div></div><p class="mt-1"><span class="sm:block"><a href="https://www.appsignal.com/python/celery-monitoring" class="no-underline hover:underline">Celery</a><span class="sm:hidden">, </span></span><span class="sm:block"><a href="https://www.appsignal.com/python/django-monitoring" class="no-underline hover:underline">Django</a><span class="sm:hidden">, </span></span><span class="sm:block"><a href="https://www.appsignal.com/python/fastapi-monitoring" class="no-underline hover:underline">FastAPI</a><span class="sm:hidden">, </span></span><span class="sm:block"><a href="https://www.appsignal.com/python/flask-monitoring" class="no-underline hover:underline">Flask</a><span class="sm:hidden">, </span></span><span class="sm:block"><a href="https://www.appsignal.com/python/jinja2-monitoring" class="no-underline hover:underline">Jinja2</a><span class="sm:hidden">, </span></span><span class="sm:block"><a href="https://www.appsignal.com/python/mysql-monitoring" class="no-underline hover:underline">MySQL</a><span class="sm:hidden">, </span></span><span class="sm:block"><a href="https://www.appsignal.com/python/pika-monitoring" class="no-underline hover:underline">Pika</a><span class="sm:hidden">, </span></span><span class="sm:block"><a href="https://www.appsignal.com/python/postgresql-monitoring" class="no-underline hover:underline">PostgreSQL</a><span class="sm:hidden">, </span></span><span class="sm:block"><a href="https://www.appsignal.com/python/redis-monitoring" class="no-underline hover:underline">Redis</a><span class="sm:hidden">, </span></span><span class="sm:block"><a href="https://www.appsignal.com/python/request-monitoring" class="no-underline hover:underline">Request</a><span class="sm:hidden">, </span></span><span class="sm:block"><a href="https://www.appsignal.com/python/sqlalchemy-monitoring" class="no-underline hover:underline">SQLAlchemy</a><span class="sm:hidden">, </span></span><span class="sm:block"><a href="https://www.appsignal.com/python/sqlite-monitoring" class="no-underline hover:underline">SQLite</a><span class="sm:hidden">, </span></span><span class="sm:block"><a href="https://www.appsignal.com/python/starlette-monitoring" class="no-underline hover:underline">Starlette</a><span class="sm:hidden">, </span></span><span class="sm:block"><a href="https://www.appsignal.com/python/wsgi-asgi-monitoring" class="no-underline hover:underline">WSGI and ASGI</a><span class="sm:hidden"></span></span></p></li></ul><ul class="space-y-2"><li><div class="flex items-center space-x-2"><div><a class="no-underline text-white text-lg sm:text-lg hover:underline"></a></div></div></li></ul></div></div></div><div class="col-span-1 sm:col-span-2 xl:col-span-4 border-t border-gray-800 pt-6 xl:flex xl:items-center xl:justify-between"><div class="w-[168px] h-[31px] mb-4 xl:mb-0"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 350.92 64.54"><g fill="#fff"><path d="M341.56 12.58c-5.46 0-9.37 4.82-13.39 10.18C325.68 11.38 322.9 0 314.07 0S302.46 11.38 300 22.76c-4-5.36-7.93-10.18-13.39-10.18-3.47 0-9.35 2.37-9.35 11.22v16.11c0 8.69 4.64 10.82 7.42 11.54 4.8 1.25 16.36 2.4 29.37 2.4s24.57-1.15 29.36-2.4c2.79-.72 7.43-2.85 7.43-11.54V23.8c0-8.85-5.88-11.22-9.36-11.22M314.07 5c5.29 0 7.3 10.44 10.12 23-3.09 3.79-6.36 6.86-10.12 6.86S307 31.78 304 28c2.63-11.72 4.65-23 10.12-23m-31.84 34.91V23.8c0-5.59 3-6.2 4.33-6.2 3.85 0 8 6.43 11.94 11.59-2.83 11.71-5.78 18.81-12.75 17.37-1.82-.5-3.52-1.65-3.52-6.65M296 48.05c3-3.35 5-8.58 6.5-14.12 3.23 3.38 6.95 5.93 11.57 5.93s8.33-2.55 11.56-5.93c1.5 5.54 3.47 10.77 6.55 14.12-5.39.5-11.88.79-18.11.79s-12.73-.29-18.12-.79m49.91-8.14c0 5-1.71 6.15-3.52 6.65-7 1.48-9.93-5.66-12.72-17.37 3.95-5.16 8.09-11.59 11.94-11.59 1.3 0 4.34.61 4.34 6.2ZM1.13 52.07a1.12 1.12 0 0 1-.79-.34 1.16 1.16 0 0 1-.34-.85 1.52 1.52 0 0 1 .11-.52L14 13.74a2 2 0 0 1 .62-1 1.81 1.81 0 0 1 1.25-.4h5a1.81 1.81 0 0 1 1.25.4 2.44 2.44 0 0 1 .68 1l13.82 36.62a2.52 2.52 0 0 1 .06.52 1.16 1.16 0 0 1-.34.85 1.12 1.12 0 0 1-.79.34h-3.92a1.4 1.4 0 0 1-1.07-.37 2.41 2.41 0 0 1-.46-.7l-2.89-7.48H9.47L6.63 51a2 2 0 0 1-.48.66 1.5 1.5 0 0 1-1.1.37Zm10.15-14.18H25.4l-7.09-18.82ZM42.52 62.84a1.25 1.25 0 0 1-1.3-1.31V23.89a1.24 1.24 0 0 1 1.3-1.3h3.57a1.25 1.25 0 0 1 1.31 1.3v2.38a11.6 11.6 0 0 1 3.62-3A11.35 11.35 0 0 1 56.58 22a12.67 12.67 0 0 1 5.33 1 10.13 10.13 0 0 1 3.68 2.83 12.56 12.56 0 0 1 2.24 4.26 19.79 19.79 0 0 1 .88 5.21c0 .64.06 1.31.06 2s0 1.34-.06 2a18.1 18.1 0 0 1-.82 5.16 13.14 13.14 0 0 1-2.24 4.22 10.21 10.21 0 0 1-3.71 2.89 12.56 12.56 0 0 1-5.36 1.05 11.36 11.36 0 0 1-5.36-1.16 10.55 10.55 0 0 1-3.54-3v13a1.27 1.27 0 0 1-.37.94 1.38 1.38 0 0 1-1 .37ZM55 47.3a6.63 6.63 0 0 0 4.17-1.16 6.53 6.53 0 0 0 2.18-3 14.43 14.43 0 0 0 .8-4.14 25.8 25.8 0 0 0 0-3.29 14.43 14.43 0 0 0-.8-4.14 6.58 6.58 0 0 0-2.18-3A6.69 6.69 0 0 0 55 27.35a6.44 6.44 0 0 0-6.49 4.22 12.43 12.43 0 0 0-.82 3.77q-.06 1-.06 2.16c0 .79 0 1.53.06 2.21a9.3 9.3 0 0 0 .85 3.6 7.15 7.15 0 0 0 2.38 2.86A6.83 6.83 0 0 0 55 47.3ZM75.71 62.84a1.27 1.27 0 0 1-1.3-1.31V23.89a1.25 1.25 0 0 1 1.3-1.3h3.58a1.29 1.29 0 0 1 .93.36 1.27 1.27 0 0 1 .37.94v2.38a11.53 11.53 0 0 1 3.63-3A11.29 11.29 0 0 1 89.77 22a12.64 12.64 0 0 1 5.33 1 10.16 10.16 0 0 1 3.69 2.83 12.74 12.74 0 0 1 2.21 4.3 20.21 20.21 0 0 1 .88 5.21v4a18.1 18.1 0 0 1-.83 5.16 12.94 12.94 0 0 1-2.24 4.22 10.21 10.21 0 0 1-3.71 2.89 12.53 12.53 0 0 1-5.36 1.05 11.31 11.31 0 0 1-5.35-1.16 10.49 10.49 0 0 1-3.55-3v13a1.27 1.27 0 0 1-.37.94 1.34 1.34 0 0 1-1 .37ZM88.19 47.3a6.1 6.1 0 0 0 6.35-4.19 14.43 14.43 0 0 0 .79-4.11 25.8 25.8 0 0 0 0-3.29 14.43 14.43 0 0 0-.79-4.14 6.1 6.1 0 0 0-6.35-4.19 6.44 6.44 0 0 0-6.5 4.22 12.74 12.74 0 0 0-.82 3.77c0 .64-.05 1.36-.05 2.16s0 1.53.05 2.21a9.3 9.3 0 0 0 .85 3.6 7.15 7.15 0 0 0 2.38 2.86 6.84 6.84 0 0 0 4.09 1.1ZM122.17 52.63a21.18 21.18 0 0 1-8.56-1.53 12.82 12.82 0 0 1-5.27-4 9.44 9.44 0 0 1-1.93-5.25 1.1 1.1 0 0 1 .34-.79 1.15 1.15 0 0 1 .85-.34h4a1.47 1.47 0 0 1 1 .31 2.75 2.75 0 0 1 .57.76 6.36 6.36 0 0 0 1.36 2.39 7.72 7.72 0 0 0 2.86 2 12.06 12.06 0 0 0 4.73.79q4.48 0 6.61-1.53a4.88 4.88 0 0 0 2.12-4.19 3.83 3.83 0 0 0-1.13-2.87 10.56 10.56 0 0 0-3.52-2c-1.58-.6-3.64-1.25-6.17-1.93a36.52 36.52 0 0 1-7-2.55 10.29 10.29 0 0 1-4.19-3.57 9.81 9.81 0 0 1-1.39-5.44 9.72 9.72 0 0 1 1.73-5.67 11.66 11.66 0 0 1 4.93-3.94 18.66 18.66 0 0 1 7.68-1.45 19 19 0 0 1 6.35 1 14 14 0 0 1 4.57 2.55 11.53 11.53 0 0 1 2.77 3.4 8.31 8.31 0 0 1 1 3.57 1.18 1.18 0 0 1-.32.77 1.09 1.09 0 0 1-.87.37h-4.14a1.83 1.83 0 0 1-.88-.23 1.5 1.5 0 0 1-.65-.85 5.22 5.22 0 0 0-2.44-3.46 9.49 9.49 0 0 0-5.39-1.42 10.19 10.19 0 0 0-5.41 1.28 4.35 4.35 0 0 0-2.07 4 4.24 4.24 0 0 0 1 2.89 8.38 8.38 0 0 0 3.18 2 58.53 58.53 0 0 0 5.75 1.9 43.26 43.26 0 0 1 7.76 2.54 10.38 10.38 0 0 1 4.4 3.51 9.64 9.64 0 0 1 1.41 5.42 10 10 0 0 1-1.95 6.2 12.32 12.32 0 0 1-5.44 4 22 22 0 0 1-8.25 1.36ZM143.77 17.37a1.34 1.34 0 0 1-1-.37 1.24 1.24 0 0 1-.37-.93v-3.58a1.34 1.34 0 0 1 .37-1 1.32 1.32 0 0 1 1-.4h4.31a1.39 1.39 0 0 1 1 .4 1.29 1.29 0 0 1 .4 1v3.58a1.2 1.2 0 0 1-.4.93 1.41 1.41 0 0 1-1 .37Zm.34 34.7a1.38 1.38 0 0 1-1-.37 1.27 1.27 0 0 1-.37-.94V23.89a1.27 1.27 0 0 1 .37-.94 1.37 1.37 0 0 1 1-.36h3.69a1.25 1.25 0 0 1 1.3 1.3v26.87a1.27 1.27 0 0 1-.37.94 1.28 1.28 0 0 1-.93.37ZM168.5 64.54a17.5 17.5 0 0 1-6.58-1.05 12.16 12.16 0 0 1-4-2.52 9.27 9.27 0 0 1-2.09-2.97 8.1 8.1 0 0 1-.65-2.38 1.13 1.13 0 0 1 .34-1 1.3 1.3 0 0 1 1-.39h3.86a1.51 1.51 0 0 1 .85.22 1.8 1.8 0 0 1 .56 1 11.19 11.19 0 0 0 1 1.73 5 5 0 0 0 1.93 1.61 7.69 7.69 0 0 0 3.57.68 11 11 0 0 0 4.05-.65 4.82 4.82 0 0 0 2.5-2.3 9.59 9.59 0 0 0 .87-4.47v-3.84a10.93 10.93 0 0 1-3.64 2.79 11.93 11.93 0 0 1-5.38 1.08 12.69 12.69 0 0 1-5.36-1 9.78 9.78 0 0 1-3.69-2.86 13.36 13.36 0 0 1-2.21-4.28 18.82 18.82 0 0 1-.82-5.27q-.06-1.53 0-3.12a18.94 18.94 0 0 1 .8-5.16 12.85 12.85 0 0 1 2.21-4.39 10.34 10.34 0 0 1 3.71-2.94 12.23 12.23 0 0 1 5.36-1.06 11.1 11.1 0 0 1 5.52 1.25 11.77 11.77 0 0 1 3.66 3.06V24a1.37 1.37 0 0 1 .37-1 1.31 1.31 0 0 1 1-.39h3.57a1.33 1.33 0 0 1 1.35 1.39v27.39a16.14 16.14 0 0 1-1.38 6.91 10 10 0 0 1-4.4 4.59 16.32 16.32 0 0 1-7.88 1.65Zm-.17-17.69a6.62 6.62 0 0 0 4.11-1.19 7.39 7.39 0 0 0 2.33-3 10.87 10.87 0 0 0 .87-3.71c0-.49.06-1.12.06-1.9s0-1.39-.06-1.84a11.09 11.09 0 0 0-.87-3.74 7.13 7.13 0 0 0-2.33-3 6.74 6.74 0 0 0-4.11-1.16 6.85 6.85 0 0 0-4.17 1.16 6.49 6.49 0 0 0-2.24 3 14.43 14.43 0 0 0-.79 4.14v2.84a14.62 14.62 0 0 0 .79 4.11 6.52 6.52 0 0 0 2.24 3.06 6.85 6.85 0 0 0 4.17 1.23ZM190.13 52.07a1.38 1.38 0 0 1-1-.37 1.31 1.31 0 0 1-.37-.94V23.89a1.31 1.31 0 0 1 .37-.94 1.37 1.37 0 0 1 1-.36h3.63a1.25 1.25 0 0 1 1.31 1.3v2.44a12.29 12.29 0 0 1 3.8-3.09 11.91 11.91 0 0 1 5.72-1.24 11.34 11.34 0 0 1 6.12 1.59 10.28 10.28 0 0 1 3.92 4.41 15.55 15.55 0 0 1 1.37 6.77v16a1.25 1.25 0 0 1-.4.94 1.39 1.39 0 0 1-1 .37h-3.92a1.28 1.28 0 0 1-.93-.37 1.27 1.27 0 0 1-.37-.94V35.06a8.33 8.33 0 0 0-1.79-5.67 6.45 6.45 0 0 0-5.13-2 6.84 6.84 0 0 0-5.18 2 7.83 7.83 0 0 0-2 5.67v15.7a1.22 1.22 0 0 1-.4.94 1.37 1.37 0 0 1-1 .37ZM230.92 52.63a11.76 11.76 0 0 1-5.25-1.16 9.93 9.93 0 0 1-3.74-3.09 7.31 7.31 0 0 1-1.39-4.38 7.42 7.42 0 0 1 3.18-6.27 18.1 18.1 0 0 1 8.39-3.09l8-1.19v-1.4a5.5 5.5 0 0 0-1.36-4c-.91-.94-2.42-1.41-4.54-1.41a6.9 6.9 0 0 0-3.74.9 5.22 5.22 0 0 0-2 2.44 1.32 1.32 0 0 1-1.25.74h-3.51a1.2 1.2 0 0 1-.94-.34 1.34 1.34 0 0 1-.31-.91 5.88 5.88 0 0 1 .71-2.12 8.28 8.28 0 0 1 2.07-2.5 11.76 11.76 0 0 1 3.66-2 15.47 15.47 0 0 1 5.35-.82 17.06 17.06 0 0 1 5.9.88 9.93 9.93 0 0 1 3.8 2.35 9.17 9.17 0 0 1 2.07 3.4 12.46 12.46 0 0 1 .65 4v18.1a1.22 1.22 0 0 1-.4.94 1.37 1.37 0 0 1-1 .37h-3.63a1.25 1.25 0 0 1-1.3-1.31v-2.38a9.3 9.3 0 0 1-2 2 10.74 10.74 0 0 1-3 1.62 13.85 13.85 0 0 1-4.42.63Zm1.59-4.82a8.24 8.24 0 0 0 3.91-.93A6.74 6.74 0 0 0 239.2 44a10.38 10.38 0 0 0 1-4.88v-1.4l-6.13 1a13.25 13.25 0 0 0-5.52 1.75 3.66 3.66 0 0 0-1.85 3.06 3.46 3.46 0 0 0 .85 2.41 5.33 5.33 0 0 0 2.16 1.42 8 8 0 0 0 2.8.45ZM254.47 52.07a1.25 1.25 0 0 1-1.3-1.31V13.12a1.25 1.25 0 0 1 1.3-1.31h3.74a1.25 1.25 0 0 1 1.3 1.31v37.64a1.25 1.25 0 0 1-1.3 1.31Z"></path></g></svg></div><ul><li class="mb-2 xl:mb-0 sm:inline-block sm:mr-4 sm:last:mr-0 xl:mr-12"><a href="https://www.appsignal.com/terms" class="no-underline hover:underline">Terms &amp; Conditions</a></li><li class="mb-2 xl:mb-0 sm:inline-block sm:mr-4 sm:last:mr-0 xl:mr-12"><a href="https://www.appsignal.com/privacy-policy" class="no-underline hover:underline">Privacy Policy</a></li><li class="mb-2 xl:mb-0 sm:inline-block sm:mr-4 sm:last:mr-0 xl:mr-12"><a href="https://www.appsignal.com/cookie-policy" class="no-underline hover:underline">Cookie Policy</a></li><li class="mb-2 xl:mb-0 sm:inline-block sm:mr-4 sm:last:mr-0 xl:mr-12"><a href="https://docs.appsignal.com/appsignal/gdpr.html" class="no-underline hover:underline">GDPR compliance</a></li><li class="mb-2 xl:mb-0 sm:inline-block sm:mr-4 sm:last:mr-0 xl:mr-12"><a href="https://www.appsignal.com/contact" class="no-underline hover:underline">Contact us / Imprint</a></li></ul></div></div></div></footer></div><script id="__NEXT_DATA__" type="application/json">{"props":{"pageProps":{"post":{"categoryConfig":{"name":"Elixir","theme":{"style":{"textColor":"text-purple-500","textColorLight":"text-purple-200","bgColor":"bg-purple-800","bgColorVariant":"bg-purple-700"},"menu":"c_h-tabs__tab--active c_h-tabs__tab--purple"}},"authors":[{"about":"Guest author Alex Koutmos is a Senior Software Engineer who writes backends in Elixir, frontends in VueJS and deploys his apps using Kubernetes. When he is not programming or blogging he is wrenching on his 1976 Datsun 280z.","name":"Alex Koutmos","image":"/images/authors/alex.jpg","slug":"alex-koutmos"}],"series":[],"slug":"2020/03/24/how-to-use-grpc-in-elixir","socialImg":"https://ondemand.bannerbear.com/signedurl/vYR1M6Lyq22EAnXbgZ/image.jpg?modifications=W3sibmFtZSI6InRpdGxlIiwidGV4dCI6IkhvdyB0byBVc2UgZ1JQQyBpbiBFbGl4aXIifSx7Im5hbWUiOiJpbWFnZSIsImltYWdlX3VybCI6Imh0dHBzOi8vYXBwc2lnbmFsLW5leHRqcy1ibG9nLTdoYzlub2p5ZC1hcHBzaWduYWwudmVyY2VsLmFwcC9pbWFnZXMvYmxvZy8yMDIwLTAzL2hvdy10by11c2UtZ3JwYy1pbi1lbGl4aXIuanBnIn0seyJuYW1lIjoiY2F0ZWdvcnlfbG9nbyIsImltYWdlX3VybCI6Imh0dHBzOi8vYXBwc2lnbmFsLW5leHRqcy1ibG9nLTdoYzlub2p5ZC1hcHBzaWduYWwudmVyY2VsLmFwcC9pbWFnZXMvbG9nb3MvZWxpeGlyLWxvZ28ucG5nIn1d\u0026s=35074699d9c0ceffe6529df8a7aae24219244bc27297a424661ef05cd24efed3","jsonLd":{"type":"BlogPosting","url":"https://blog.appsignal.com/2020/03/24/how-to-use-grpc-in-elixir.html","title":"How to Use gRPC in Elixir","description":"Learn about gRPC and how to use it in an Elixir application.","images":["https://ondemand.bannerbear.com/signedurl/vYR1M6Lyq22EAnXbgZ/image.jpg?modifications=W3sibmFtZSI6InRpdGxlIiwidGV4dCI6IkhvdyB0byBVc2UgZ1JQQyBpbiBFbGl4aXIifSx7Im5hbWUiOiJpbWFnZSIsImltYWdlX3VybCI6Imh0dHBzOi8vYXBwc2lnbmFsLW5leHRqcy1ibG9nLTdoYzlub2p5ZC1hcHBzaWduYWwudmVyY2VsLmFwcC9pbWFnZXMvYmxvZy8yMDIwLTAzL2hvdy10by11c2UtZ3JwYy1pbi1lbGl4aXIuanBnIn0seyJuYW1lIjoiY2F0ZWdvcnlfbG9nbyIsImltYWdlX3VybCI6Imh0dHBzOi8vYXBwc2lnbmFsLW5leHRqcy1ibG9nLTdoYzlub2p5ZC1hcHBzaWduYWwudmVyY2VsLmFwcC9pbWFnZXMvbG9nb3MvZWxpeGlyLWxvZ28ucG5nIn1d\u0026amp;s=35074699d9c0ceffe6529df8a7aae24219244bc27297a424661ef05cd24efed3"],"datePublished":"2020-03-24T00:00:00+00:00","dateModified":"2020-03-24T00:00:00+00:00","authorName":[{"type":"Person","name":"Alex Koutmos","url":"https://blog.appsignal.com/authors/alex-koutmos.html"}],"publisherName":"AppSignal","publisherLogo":"https://appsignal.com/images/logo-appsignal.svg"},"title":"How to Use gRPC in Elixir","intro":"Learn about gRPC and how to use it in an Elixir application.","category":"elixir","tags":"elixir, elixir-alchemy","author":"Alex Koutmos","image":"/images/blog/2020-03/how-to-use-grpc-in-elixir.jpg","opacity":4,"headerType":"legacy","facebook":"/images/blog/2020-03/how-to-use-grpc-in-elixir-facebook.jpg","twitter":"/images/blog/2020-03/how-to-use-grpc-in-elixir-twitter.jpg","date":"2020-03-24T00:00:00+00:00","body":{"raw":"\nIn today's post, we'll learn what gRPC is, when you should reach for such a tool, and some of\nthe pros and cons of using it. After going over an introduction of gRPC, we'll dive\nright into a sample application where we'll build an Elixir backend API powered by gRPC.\n\nLet's jump right in!\n\n## What Is gRPC and How Does It Work?\n\ngRPC is a framework used to enable a remote procedure call (RPC) style of communication. RPC is a style of system\ncommunication where a client can directly invoke exposed methods on a server. From the client's perspective, it feels no\ndifferent than making a call to a local function or method as long as you provide the applicable parameters and handle\nthe return type appropriately. gRPC facilitates this communication by providing 2 things for you:\n\n1. A client library which can be used to invoke allowed procedures\n2. A consistent data serialization standard via Protocol Buffers\n\nLet's break these two items down so we can appreciate the inner workings of gRPC. When creating a gRPC server, you'll\nhave to define what procedures are invokable from the client, what inputs they accept, and what outputs they return.\nThis interface specification is what allows client libraries (generally called gRPC stubs) to be automatically generated\nfor various languages and runtimes as the contract for the remote procedure call is explicitly defined. This gRPC client\nlibrary can then communicate with the server using Protocol Buffers. Protocol Buffers provide a mechanism for\nserializing and deserializing the payloads (both request and response) so that you can operate on the data with types\nnative to your language. The diagram available in the gRPC documentation can help visualize [this interaction](https://grpc.io/docs/guides/):\n\n![gRPC Diagram](/images/blog/2020-03/landing-2.svg)\n_Source: [https://grpc.io](https://grpc.io)_\n\nOne thing that we haven't discussed yet is how data is transmitted between the client and the server. For this, gRPC\nleans on the HTTP/2 protocol. By using HTTP/2 as the underlying protocol, gRPC is able to support features such as\nbi-directional data streaming and several other features that are not available in HTTP/1.1.\n\n## When Would You Use gRPC Over REST/GraphQL?\n\nThe obvious question that may come to mind is: \"How does gRPC compare to REST/GraphQL, and when do I use one over the\nother?\".\n\nIn general, if you plan to use gRPC for a frontend application, there are a couple of caveats that you need to\nkeep in mind. In order to serialize and deserialize Protocol Buffer payloads from your Javascript application, you'll\nhave to leverage [grpc-web](https://github.com/grpc/grpc-web). In addition, you'll also need to run a proxy on the backend (the\ndefault supported gRPC proxy is Envoy) since browsers cannot talk directly to gRPC servers. Depending on your resources\nand time constraints, this may be a show stopper; in which case, REST and GraphQL will do just fine.\n\nIf frontend application communication is not a requirement, and instead what you require is inter-microservice\ncommunication from within your service cluster, then the barrier to entry for gRPC gets lowered considerably. At the\ntime of writing, gRPC currently has client libraries and tooling for most mainstream languages including Elixir, Python,\nC++, Go, Ruby, to name a few. I would argue that the barrier to entry for consuming a RESTful API is still much lower\nthan consuming a gRPC service given that all you need for the former is an HTTP client, which is baked into most\nlanguages and runtimes these days.\n\nOn the other hand, if you are willing to make the investment, you do get the added benefits of having your responses and\nrequests checked against the Protocol Buffer specification that is used for code generation. This, in turn, provides you\nwith some guarantees as to what you can expect on the client-side and the server-side. This guarantee and\nintrospection is also something that you get with GraphQL when you define your server schemas. An added benefit of\nGraphQL over gRPC is that you are able to dynamically request embedded properties from within your schema depending on\nthe query that you make to your backend server.\n\nLike most things in the software engineering field, the technology you choose will largely depend on your application.\nBelow are my personal TL;DR rules of thumb regarding the various technologies:\n\ngRPC:\n\n- Use: When communicating between microservices in my service cluster or if performance is a requirement\n- Don't use: When I need to transmit data from the browser to the backend\n\nGraphQL:\n\n- Use: When I need to aggregate data from multiple microservices for the purposes of streamlining frontend development,\n or if my frontend data requirements are dynamic\n- Don't use: When communicating between microservices in my service cluster, or if my API needs to be used by the lowest\n common denominator of consumers\n\nREST:\n\n- Use: When I need to put something together quickly or if I need to cater to the lowest common denominator of consumers\n- Don't use: If I require any kind of type checking or if I want to reduce payload sizes over the wire\n\n## Experimenting With gRPC in Elixir\n\nWith all the theory out of the way, it's time to get our hands dirty and experiment with wiring up a gRPC server. In\norder to keep us focused on the gRPC experience, we'll opt for having a backend powered by an Agent versus an actual\ndatabase, but all of the concepts should be easily transferable to an application backed by Postgres, for example. Our\ngRPC application will be a simple user management service where we can create and fetch users. After creating our\nElixir service, we'll interact with it via grpcurl, which is effectively cURL, but for gRPC. If at any point you get\nstuck or require the source code, feel free to [check it out on GitHub](https://github.com/akoutmos/sample_app). With all that being said, let's dive right in!\n\nBefore creating our Elixir project, there are a couple of things that we require on our machine in order to properly\ndevelop and test our application. We'll first need to install `protoc` so that `.proto` files can be compiled\nappropriately. If you are on an OSX machine, you can run `brew install protobuf`, otherwise, [see instructions](https://github.com/protocolbuffers/protobuf/blob/master/src/README.md) specific to\nyour platform. Now, with `protoc` available on your machine, you'll also want to install `grpcurl` so that you can\ninteract with the application. Once again, if you are on an OSX machine, you can run `brew install grpcurl`, otherwise, [check for instructions specific to your platform](https://github.com/fullstorydev/grpcurl#installation).\n\n\u003cBanner lang=\"elixir\" /\u003e\n\nLastly, you'll want to run `mix escript.install hex protobuf` and ensure that `protoc-gen-elixir` script is available on\nyour path (if you use ASDF as your runtime version manager, this requires running `asdf reshim elixir`). With all that\nboilerplate done, you can run `mix new sample_app --sup` to get a new application started.\n\nOnce inside your sample application directory, you'll want to update your `mix.exs` file to include our gRPC related\ndependencies. For this application, we will be leveraging https://github.com/elixir-grpc/grpc and\nhttps://github.com/tony612/protobuf-elixir. In order to bring these two dependencies into your project, ensure that\nyour `deps/0` function looks like this:\n\n```elixir\ndefp deps do\n [\n {:grpc, \"~\u003e 0.5.0-beta\"},\n {:cowlib, \"~\u003e 2.8.0\", hex: :grpc_cowlib, override: true}\n ]\nend\n```\n\nWith that in place, run `mix deps.get` from the terminal to pull down all necessary project\ndependencies. Next, you'll want to create a configuration file at `config/config.exs` with the following content:\n\n```elixir\nuse Mix.Config\n\n# Configures Elixir's Logger\nconfig :logger, :console, format: \"$time $metadata[$level] $message\\n\"\n\nconfig :grpc, start_server: true\n```\n\nNext, we'll want to create the required Protocol Buffer definitions for our application. The [Protocol Buffer specification](https://developers.google.com/protocol-buffers/docs/proto3) is fairly large and we'll only be using a small subset of it to keep things simple. Create a file\n`sample_app.proto` at the root of your project with the following content:\n\n```\nsyntax = \"proto3\";\n\npackage sample_app;\n\nservice User {\n rpc Create (CreateRequest) returns (UserReply) {}\n rpc Get (GetRequest) returns (UserReply) {}\n}\n\nmessage UserReply {\n int32 id = 1;\n string first_name = 2;\n string last_name = 3;\n int32 age = 4;\n}\n\nmessage CreateRequest {\n string first_name = 1;\n string last_name = 2;\n int32 age = 3;\n}\n\nmessage GetRequest {\n int32 id = 1;\n}\n```\n\nAs you can see, our Protocol Buffer definition is fairly straightforward and easy to read. We define a service that\nexposes two RPC methods—`Create` and `Get`. We also define the types that each of those RPC calls takes as\ninput and returns as a result. With the `sample_app.proto` file in place, we'll want to open up a terminal and run the\nfollowing:\n\n```shell\n$ protoc --elixir_out=plugins=grpc:./lib sample_app.proto\n```\n\nYou'll notice that this command produces a file `lib/sample_app.pb.ex` with several modules within it. If you look\ncarefully, you'll see that the code that was generated is the Elixir representation of the `sample_app.proto` file that\nwe wrote. It contains all of the types that we defined along with the RPC method definitions. With our auto-generated\ncode in place, let's get to work on the actual RPC handlers.\n\nAs previously mentioned, we'll be using an Agent to persist state across gRPC calls instead of a database, for the sake\nof simplicity. Our agent will have the ability to look up users via their ID, and will also be able to create new users.\nCreate a file `lib/user_db.ex` with the following code which provides that functionality:\n\n```elixir\ndefmodule UserDB do\n use Agent\n\n def start_link(_) do\n Agent.start_link(\n fn -\u003e\n {%{}, 1}\n end,\n name: __MODULE__\n )\n end\n\n def add_user(user) do\n Agent.get_and_update(__MODULE__, fn {users_map, next_id} -\u003e\n updated_users_map = Map.put(users_map, next_id, user)\n\n {Map.put(user, :id, next_id), {updated_users_map, next_id + 1}}\n end)\n end\n\n def get_user(id) do\n Agent.get(__MODULE__, fn {users_map, _next_id} -\u003e\n Map.get(users_map, id)\n end)\n end\nend\n```\n\nWith that in place, we can create our RPC handlers for creating and getting users. Create a file `lib/sample_app.ex`\nwith the following content:\n\n```elixir\ndefmodule SampleApp.Endpoint do\n use GRPC.Endpoint\n\n intercept GRPC.Logger.Server\n run SampleApp.User.Server\nend\n\ndefmodule SampleApp.User.Server do\n use GRPC.Server, service: SampleApp.User.Service\n\n def create(request, _stream) do\n new_user =\n UserDB.add_user(%{\n first_name: request.first_name,\n last_name: request.last_name,\n age: request.age\n })\n\n SampleApp.UserReply.new(new_user)\n end\n\n def get(request, _stream) do\n user = UserDB.get_user(request.id)\n\n if user == nil do\n raise GRPC.RPCError, status: :not_found\n else\n SampleApp.UserReply.new(user)\n end\n end\nend\n```\n\nOur file defines two modules. The `SampleApp.Endpoint` module defines the gRPC server and provides the\nhandler module to service requests. The `SampleApp.User.Server` module contains the actual implementations of the two\nRPC calls that we defined. You'll notice that for each of the handlers, we provide the correct return type (as defined in\nour Protocol Buffer file). When we encounter an error (in this case, looking up a user that doesn't exist), we raise a\n`GRPC.RPCError` with the appropriate status code.\n\nAll that is left now is to start up our Agent and our gRPC server, and we're good to go. Open up\n`lib/sample_app/application.ex` and ensure that your process `children` list looks like this:\n\n```elixir\nchildren = [\n UserDB,\n {GRPC.Server.Supervisor, {SampleApp.Endpoint, 50051}}\n]\n```\n\nWith that in place, you should be able to run `mix grpc.server` from the terminal to start your gRPC server. In another\nterminal session (and from within the project directory), you should be able to use `grpcurl` commands to interact with\nyour application:\n\n```shell\n$ grpcurl -plaintext -proto sample_app.proto -d '{\"first_name\": \"Bob\", \"last_name\": \"Smith\", \"age\": 40}' localhost:50051 sample_app.User.Create\n{\n \"id\": 1,\n \"firstName\": \"Bob\",\n \"lastName\": \"Smith\",\n \"age\": 40\n}\n\n$ grpcurl -plaintext -proto sample_app.proto -d '{\"id\": 1}' localhost:50051 sample_app.User.Get\n{\n \"firstName\": \"Bob\",\n \"lastName\": \"Smith\",\n \"age\": 40\n}\n\n$ grpcurl -plaintext -proto sample_app.proto -d '{\"id\": 2}' localhost:50051 sample_app.User.Get\nERROR:\n Code: NotFound\n Message: Some requested entity (e.g., file or directory) was not found\n```\n\n## Conclusion\n\nThanks for sticking with me to the end. Hopefully, you learned a thing or two about gRPC and how to go about\nusing it within an Elixir application. If you would like to learn more about gRPC or any of the tools that I mentioned,\nI suggest going through the following resources:\n\n- [elixir-grpc](https://github.com/elixir-grpc/grpc)\n- [protobuf-elixir](https://github.com/tony612/protobuf-elixir)\n- [Our sample app](https://github.com/akoutmos/sample_app)\n\n**P.S. If you'd like to read Elixir Alchemy posts as soon as they get off the press, [subscribe to our Elixir Alchemy newsletter and never miss a single post](/elixir-alchemy)!**\n","code":"var Component=(()=\u003e{var p=Object.create;var t=Object.defineProperty;var h=Object.getOwnPropertyDescriptor;var y=Object.getOwnPropertyNames;var u=Object.getPrototypeOf,g=Object.prototype.hasOwnProperty;var E=(l,e)=\u003e()=\u003e(e||l((e={exports:{}}).exports,e),e.exports),m=(l,e)=\u003e{for(var r in e)t(l,r,{get:e[r],enumerable:!0})},i=(l,e,r,o)=\u003e{if(e\u0026\u0026typeof e==\"object\"||typeof e==\"function\")for(let a of y(e))!g.call(l,a)\u0026\u0026a!==r\u0026\u0026t(l,a,{get:()=\u003ee[a],enumerable:!(o=h(e,a))||o.enumerable});return l};var F=(l,e,r)=\u003e(r=l!=null?p(u(l)):{},i(e||!l||!l.__esModule?t(r,\"default\",{value:l,enumerable:!0}):r,l)),f=l=\u003ei(t({},\"__esModule\",{value:!0}),l);var c=E((v,s)=\u003e{s.exports=_jsx_runtime});var C={};m(C,{default:()=\u003eb,frontmatter:()=\u003eB});var n=F(c()),B={title:\"How to Use gRPC in Elixir\",author:\"Alex Koutmos\",intro:\"Learn about gRPC and how to use it in an Elixir application.\",facebook:\"/images/blog/2020-03/how-to-use-grpc-in-elixir-facebook.jpg\",twitter:\"/images/blog/2020-03/how-to-use-grpc-in-elixir-twitter.jpg\",image:\"/images/blog/2020-03/how-to-use-grpc-in-elixir.jpg\",opacity:4,headerType:\"legacy\",tags:\"elixir, elixir-alchemy\",category:\"elixir\"};function d(l){let e=Object.assign({p:\"p\",h2:\"h2\",ol:\"ol\",li:\"li\",a:\"a\",img:\"img\",em:\"em\",ul:\"ul\",code:\"code\",figure:\"figure\",pre:\"pre\",span:\"span\",strong:\"strong\"},l.components),{Banner:r}=e;return r||_(\"Banner\",!0),(0,n.jsxs)(n.Fragment,{children:[(0,n.jsx)(e.p,{children:`In today's post, we'll learn what gRPC is, when you should reach for such a tool, and some of\nthe pros and cons of using it. After going over an introduction of gRPC, we'll dive\nright into a sample application where we'll build an Elixir backend API powered by gRPC.`}),`\n`,(0,n.jsx)(e.p,{children:\"Let's jump right in!\"}),`\n`,(0,n.jsx)(e.h2,{id:\"what-is-grpc-and-how-does-it-work\",children:\"What Is gRPC and How Does It Work?\"}),`\n`,(0,n.jsx)(e.p,{children:`gRPC is a framework used to enable a remote procedure call (RPC) style of communication. RPC is a style of system\ncommunication where a client can directly invoke exposed methods on a server. From the client's perspective, it feels no\ndifferent than making a call to a local function or method as long as you provide the applicable parameters and handle\nthe return type appropriately. gRPC facilitates this communication by providing 2 things for you:`}),`\n`,(0,n.jsxs)(e.ol,{children:[`\n`,(0,n.jsx)(e.li,{children:\"A client library which can be used to invoke allowed procedures\"}),`\n`,(0,n.jsx)(e.li,{children:\"A consistent data serialization standard via Protocol Buffers\"}),`\n`]}),`\n`,(0,n.jsxs)(e.p,{children:[`Let's break these two items down so we can appreciate the inner workings of gRPC. When creating a gRPC server, you'll\nhave to define what procedures are invokable from the client, what inputs they accept, and what outputs they return.\nThis interface specification is what allows client libraries (generally called gRPC stubs) to be automatically generated\nfor various languages and runtimes as the contract for the remote procedure call is explicitly defined. This gRPC client\nlibrary can then communicate with the server using Protocol Buffers. Protocol Buffers provide a mechanism for\nserializing and deserializing the payloads (both request and response) so that you can operate on the data with types\nnative to your language. The diagram available in the gRPC documentation can help visualize `,(0,n.jsx)(e.a,{href:\"https://grpc.io/docs/guides/\",children:\"this interaction\"}),\":\"]}),`\n`,(0,n.jsxs)(e.p,{children:[(0,n.jsx)(e.img,{src:\"/images/blog/2020-03/landing-2.svg\",alt:\"gRPC Diagram\",width:\"552\",height:\"327\"}),`\n`,(0,n.jsxs)(e.em,{children:[\"Source: \",(0,n.jsx)(e.a,{href:\"https://grpc.io\",children:\"https://grpc.io\"})]})]}),`\n`,(0,n.jsx)(e.p,{children:`One thing that we haven't discussed yet is how data is transmitted between the client and the server. For this, gRPC\nleans on the HTTP/2 protocol. By using HTTP/2 as the underlying protocol, gRPC is able to support features such as\nbi-directional data streaming and several other features that are not available in HTTP/1.1.`}),`\n`,(0,n.jsx)(e.h2,{id:\"when-would-you-use-grpc-over-restgraphql\",children:\"When Would You Use gRPC Over REST/GraphQL?\"}),`\n`,(0,n.jsx)(e.p,{children:`The obvious question that may come to mind is: \"How does gRPC compare to REST/GraphQL, and when do I use one over the\nother?\".`}),`\n`,(0,n.jsxs)(e.p,{children:[`In general, if you plan to use gRPC for a frontend application, there are a couple of caveats that you need to\nkeep in mind. In order to serialize and deserialize Protocol Buffer payloads from your Javascript application, you'll\nhave to leverage `,(0,n.jsx)(e.a,{href:\"https://github.com/grpc/grpc-web\",children:\"grpc-web\"}),`. In addition, you'll also need to run a proxy on the backend (the\ndefault supported gRPC proxy is Envoy) since browsers cannot talk directly to gRPC servers. Depending on your resources\nand time constraints, this may be a show stopper; in which case, REST and GraphQL will do just fine.`]}),`\n`,(0,n.jsx)(e.p,{children:`If frontend application communication is not a requirement, and instead what you require is inter-microservice\ncommunication from within your service cluster, then the barrier to entry for gRPC gets lowered considerably. At the\ntime of writing, gRPC currently has client libraries and tooling for most mainstream languages including Elixir, Python,\nC++, Go, Ruby, to name a few. I would argue that the barrier to entry for consuming a RESTful API is still much lower\nthan consuming a gRPC service given that all you need for the former is an HTTP client, which is baked into most\nlanguages and runtimes these days.`}),`\n`,(0,n.jsx)(e.p,{children:`On the other hand, if you are willing to make the investment, you do get the added benefits of having your responses and\nrequests checked against the Protocol Buffer specification that is used for code generation. This, in turn, provides you\nwith some guarantees as to what you can expect on the client-side and the server-side. This guarantee and\nintrospection is also something that you get with GraphQL when you define your server schemas. An added benefit of\nGraphQL over gRPC is that you are able to dynamically request embedded properties from within your schema depending on\nthe query that you make to your backend server.`}),`\n`,(0,n.jsx)(e.p,{children:`Like most things in the software engineering field, the technology you choose will largely depend on your application.\nBelow are my personal TL;DR rules of thumb regarding the various technologies:`}),`\n`,(0,n.jsx)(e.p,{children:\"gRPC:\"}),`\n`,(0,n.jsxs)(e.ul,{children:[`\n`,(0,n.jsx)(e.li,{children:\"Use: When communicating between microservices in my service cluster or if performance is a requirement\"}),`\n`,(0,n.jsx)(e.li,{children:\"Don't use: When I need to transmit data from the browser to the backend\"}),`\n`]}),`\n`,(0,n.jsx)(e.p,{children:\"GraphQL:\"}),`\n`,(0,n.jsxs)(e.ul,{children:[`\n`,(0,n.jsx)(e.li,{children:`Use: When I need to aggregate data from multiple microservices for the purposes of streamlining frontend development,\nor if my frontend data requirements are dynamic`}),`\n`,(0,n.jsx)(e.li,{children:`Don't use: When communicating between microservices in my service cluster, or if my API needs to be used by the lowest\ncommon denominator of consumers`}),`\n`]}),`\n`,(0,n.jsx)(e.p,{children:\"REST:\"}),`\n`,(0,n.jsxs)(e.ul,{children:[`\n`,(0,n.jsx)(e.li,{children:\"Use: When I need to put something together quickly or if I need to cater to the lowest common denominator of consumers\"}),`\n`,(0,n.jsx)(e.li,{children:\"Don't use: If I require any kind of type checking or if I want to reduce payload sizes over the wire\"}),`\n`]}),`\n`,(0,n.jsx)(e.h2,{id:\"experimenting-with-grpc-in-elixir\",children:\"Experimenting With gRPC in Elixir\"}),`\n`,(0,n.jsxs)(e.p,{children:[`With all the theory out of the way, it's time to get our hands dirty and experiment with wiring up a gRPC server. In\norder to keep us focused on the gRPC experience, we'll opt for having a backend powered by an Agent versus an actual\ndatabase, but all of the concepts should be easily transferable to an application backed by Postgres, for example. Our\ngRPC application will be a simple user management service where we can create and fetch users. After creating our\nElixir service, we'll interact with it via grpcurl, which is effectively cURL, but for gRPC. If at any point you get\nstuck or require the source code, feel free to `,(0,n.jsx)(e.a,{href:\"https://github.com/akoutmos/sample_app\",children:\"check it out on GitHub\"}),\". With all that being said, let's dive right in!\"]}),`\n`,(0,n.jsxs)(e.p,{children:[`Before creating our Elixir project, there are a couple of things that we require on our machine in order to properly\ndevelop and test our application. We'll first need to install `,(0,n.jsx)(e.code,{children:\"protoc\"}),\" so that \",(0,n.jsx)(e.code,{children:\".proto\"}),` files can be compiled\nappropriately. If you are on an OSX machine, you can run `,(0,n.jsx)(e.code,{children:\"brew install protobuf\"}),\", otherwise, \",(0,n.jsx)(e.a,{href:\"https://github.com/protocolbuffers/protobuf/blob/master/src/README.md\",children:\"see instructions\"}),` specific to\nyour platform. Now, with `,(0,n.jsx)(e.code,{children:\"protoc\"}),\" available on your machine, you'll also want to install \",(0,n.jsx)(e.code,{children:\"grpcurl\"}),` so that you can\ninteract with the application. Once again, if you are on an OSX machine, you can run `,(0,n.jsx)(e.code,{children:\"brew install grpcurl\"}),\", otherwise, \",(0,n.jsx)(e.a,{href:\"https://github.com/fullstorydev/grpcurl#installation\",children:\"check for instructions specific to your platform\"}),\".\"]}),`\n`,(0,n.jsx)(r,{lang:\"elixir\"}),`\n`,(0,n.jsxs)(e.p,{children:[\"Lastly, you'll want to run \",(0,n.jsx)(e.code,{children:\"mix escript.install hex protobuf\"}),\" and ensure that \",(0,n.jsx)(e.code,{children:\"protoc-gen-elixir\"}),` script is available on\nyour path (if you use ASDF as your runtime version manager, this requires running `,(0,n.jsx)(e.code,{children:\"asdf reshim elixir\"}),`). With all that\nboilerplate done, you can run `,(0,n.jsx)(e.code,{children:\"mix new sample_app --sup\"}),\" to get a new application started.\"]}),`\n`,(0,n.jsxs)(e.p,{children:[\"Once inside your sample application directory, you'll want to update your \",(0,n.jsx)(e.code,{children:\"mix.exs\"}),` file to include our gRPC related\ndependencies. For this application, we will be leveraging `,(0,n.jsx)(e.a,{href:\"https://github.com/elixir-grpc/grpc\",children:\"https://github.com/elixir-grpc/grpc\"}),` and\n`,(0,n.jsx)(e.a,{href:\"https://github.com/tony612/protobuf-elixir\",children:\"https://github.com/tony612/protobuf-elixir\"}),`. In order to bring these two dependencies into your project, ensure that\nyour `,(0,n.jsx)(e.code,{children:\"deps/0\"}),\" function looks like this:\"]}),`\n`,(0,n.jsx)(e.figure,{\"data-rehype-pretty-code-figure\":\"\",children:(0,n.jsx)(e.pre,{tabIndex:\"0\",\"data-language\":\"elixir\",\"data-theme\":\"github-dark\",\"data-raw\":`defp deps do\n [\n {:grpc, \"~\u003e 0.5.0-beta\"},\n {:cowlib, \"~\u003e 2.8.0\", hex: :grpc_cowlib, override: true}\n ]\nend\n`,children:(0,n.jsxs)(e.code,{\"data-language\":\"elixir\",\"data-theme\":\"github-dark\",style:{display:\"grid\"},children:[(0,n.jsxs)(e.span,{\"data-line\":\"\",children:[(0,n.jsx)(e.span,{style:{color:\"#F97583\"},children:\"defp\"}),(0,n.jsx)(e.span,{style:{color:\"#B392F0\"},children:\" deps\"}),(0,n.jsx)(e.span,{style:{color:\"#F97583\"},children:\" do\"})]}),`\n`,(0,n.jsx)(e.span,{\"data-line\":\"\",children:(0,n.jsx)(e.span,{style:{color:\"#E1E4E8\"},children:\" [\"})}),`\n`,(0,n.jsxs)(e.span,{\"data-line\":\"\",children:[(0,n.jsx)(e.span,{style:{color:\"#E1E4E8\"},children:\" {\"}),(0,n.jsx)(e.span,{style:{color:\"#79B8FF\"},children:\":grpc\"}),(0,n.jsx)(e.span,{style:{color:\"#E1E4E8\"},children:\", \"}),(0,n.jsx)(e.span,{style:{color:\"#9ECBFF\"},children:'\"~\u003e 0.5.0-beta\"'}),(0,n.jsx)(e.span,{style:{color:\"#E1E4E8\"},children:\"},\"})]}),`\n`,(0,n.jsxs)(e.span,{\"data-line\":\"\",children:[(0,n.jsx)(e.span,{style:{color:\"#E1E4E8\"},children:\" {\"}),(0,n.jsx)(e.span,{style:{color:\"#79B8FF\"},children:\":cowlib\"}),(0,n.jsx)(e.span,{style:{color:\"#E1E4E8\"},children:\", \"}),(0,n.jsx)(e.span,{style:{color:\"#9ECBFF\"},children:'\"~\u003e 2.8.0\"'}),(0,n.jsx)(e.span,{style:{color:\"#E1E4E8\"},children:\", \"}),(0,n.jsx)(e.span,{style:{color:\"#79B8FF\"},children:\"hex:\"}),(0,n.jsx)(e.span,{style:{color:\"#79B8FF\"},children:\" :grpc_cowlib\"}),(0,n.jsx)(e.span,{style:{color:\"#E1E4E8\"},children:\", \"}),(0,n.jsx)(e.span,{style:{color:\"#79B8FF\"},children:\"override:\"}),(0,n.jsx)(e.span,{style:{color:\"#79B8FF\"},children:\" true\"}),(0,n.jsx)(e.span,{style:{color:\"#E1E4E8\"},children:\"}\"})]}),`\n`,(0,n.jsx)(e.span,{\"data-line\":\"\",children:(0,n.jsx)(e.span,{style:{color:\"#E1E4E8\"},children:\" ]\"})}),`\n`,(0,n.jsx)(e.span,{\"data-line\":\"\",children:(0,n.jsx)(e.span,{style:{color:\"#F97583\"},children:\"end\"})})]})})}),`\n`,(0,n.jsxs)(e.p,{children:[\"With that in place, run \",(0,n.jsx)(e.code,{children:\"mix deps.get\"}),` from the terminal to pull down all necessary project\ndependencies. Next, you'll want to create a configuration file at `,(0,n.jsx)(e.code,{children:\"config/config.exs\"}),\" with the following content:\"]}),`\n`,(0,n.jsx)(e.figure,{\"data-rehype-pretty-code-figure\":\"\",children:(0,n.jsx)(e.pre,{tabIndex:\"0\",\"data-language\":\"elixir\",\"data-theme\":\"github-dark\",\"data-raw\":`use Mix.Config\n\n# Configures Elixir's Logger\nconfig :logger, :console, format: \"$time $metadata[$level] $message\\\\n\"\n\nconfig :grpc, start_server: true\n`,children:(0,n.jsxs)(e.code,{\"data-language\":\"elixir\",\"data-theme\":\"github-dark\",style:{display:\"grid\"},children:[(0,n.jsxs)(e.span,{\"data-line\":\"\",children:[(0,n.jsx)(e.span,{style:{color:\"#F97583\"},children:\"use\"}),(0,n.jsx)(e.span,{style:{color:\"#B392F0\"},children:\" Mix\"}),(0,n.jsx)(e.span,{style:{color:\"#E1E4E8\"},children:\".\"}),(0,n.jsx)(e.span,{style:{color:\"#B392F0\"},children:\"Config\"})]}),`\n`,(0,n.jsx)(e.span,{\"data-line\":\"\",children:\" \"}),`\n`,(0,n.jsx)(e.span,{\"data-line\":\"\",children:(0,n.jsx)(e.span,{style:{color:\"#6A737D\"},children:\"# Configures Elixir's Logger\"})}),`\n`,(0,n.jsxs)(e.span,{\"data-line\":\"\",children:[(0,n.jsx)(e.span,{style:{color:\"#E1E4E8\"},children:\"config \"}),(0,n.jsx)(e.span,{style:{color:\"#79B8FF\"},children:\":logger\"}),(0,n.jsx)(e.span,{style:{color:\"#E1E4E8\"},children:\", \"}),(0,n.jsx)(e.span,{style:{color:\"#79B8FF\"},children:\":console\"}),(0,n.jsx)(e.span,{style:{color:\"#E1E4E8\"},children:\", \"}),(0,n.jsx)(e.span,{style:{color:\"#79B8FF\"},children:\"format:\"}),(0,n.jsx)(e.span,{style:{color:\"#9ECBFF\"},children:' \"$time $metadata[$level] $message'}),(0,n.jsx)(e.span,{style:{color:\"#79B8FF\"},children:\"\\\\n\"}),(0,n.jsx)(e.span,{style:{color:\"#9ECBFF\"},children:'\"'})]}),`\n`,(0,n.jsx)(e.span,{\"data-line\":\"\",children:\" \"}),`\n`,(0,n.jsxs)(e.span,{\"data-line\":\"\",children:[(0,n.jsx)(e.span,{style:{color:\"#E1E4E8\"},children:\"config \"}),(0,n.jsx)(e.span,{style:{color:\"#79B8FF\"},children:\":grpc\"}),(0,n.jsx)(e.span,{style:{color:\"#E1E4E8\"},children:\", \"}),(0,n.jsx)(e.span,{style:{color:\"#79B8FF\"},children:\"start_server:\"}),(0,n.jsx)(e.span,{style:{color:\"#79B8FF\"},children:\" true\"})]})]})})}),`\n`,(0,n.jsxs)(e.p,{children:[\"Next, we'll want to create the required Protocol Buffer definitions for our application. The \",(0,n.jsx)(e.a,{href:\"https://developers.google.com/protocol-buffers/docs/proto3\",children:\"Protocol Buffer specification\"}),` is fairly large and we'll only be using a small subset of it to keep things simple. Create a file\n`,(0,n.jsx)(e.code,{children:\"sample_app.proto\"}),\" at the root of your project with the following content:\"]}),`\n`,(0,n.jsx)(e.pre,{\"data-raw\":`syntax = \"proto3\";\n\npackage sample_app;\n\nservice User {\n rpc Create (CreateRequest) returns (UserReply) {}\n rpc Get (GetRequest) returns (UserReply) {}\n}\n\nmessage UserReply {\n int32 id = 1;\n string first_name = 2;\n string last_name = 3;\n int32 age = 4;\n}\n\nmessage CreateRequest {\n string first_name = 1;\n string last_name = 2;\n int32 age = 3;\n}\n\nmessage GetRequest {\n int32 id = 1;\n}\n`,children:(0,n.jsx)(e.code,{children:`syntax = \"proto3\";\n\npackage sample_app;\n\nservice User {\n rpc Create (CreateRequest) returns (UserReply) {}\n rpc Get (GetRequest) returns (UserReply) {}\n}\n\nmessage UserReply {\n int32 id = 1;\n string first_name = 2;\n string last_name = 3;\n int32 age = 4;\n}\n\nmessage CreateRequest {\n string first_name = 1;\n string last_name = 2;\n int32 age = 3;\n}\n\nmessage GetRequest {\n int32 id = 1;\n}\n`})}),`\n`,(0,n.jsxs)(e.p,{children:[`As you can see, our Protocol Buffer definition is fairly straightforward and easy to read. We define a service that\nexposes two RPC methods\\u2014`,(0,n.jsx)(e.code,{children:\"Create\"}),\" and \",(0,n.jsx)(e.code,{children:\"Get\"}),`. We also define the types that each of those RPC calls takes as\ninput and returns as a result. With the `,(0,n.jsx)(e.code,{children:\"sample_app.proto\"}),` file in place, we'll want to open up a terminal and run the\nfollowing:`]}),`\n`,(0,n.jsx)(e.figure,{\"data-rehype-pretty-code-figure\":\"\",children:(0,n.jsx)(e.pre,{tabIndex:\"0\",\"data-language\":\"shell\",\"data-theme\":\"github-dark\",\"data-raw\":`$ protoc --elixir_out=plugins=grpc:./lib sample_app.proto\n`,children:(0,n.jsx)(e.code,{\"data-language\":\"shell\",\"data-theme\":\"github-dark\",style:{display:\"grid\"},children:(0,n.jsxs)(e.span,{\"data-line\":\"\",children:[(0,n.jsx)(e.span,{style:{color:\"#B392F0\"},children:\"$\"}),(0,n.jsx)(e.span,{style:{color:\"#9ECBFF\"},children:\" protoc\"}),(0,n.jsx)(e.span,{style:{color:\"#79B8FF\"},children:\" --elixir_out=plugins=grpc:./lib\"}),(0,n.jsx)(e.span,{style:{color:\"#9ECBFF\"},children:\" sample_app.proto\"})]})})})}),`\n`,(0,n.jsxs)(e.p,{children:[\"You'll notice that this command produces a file \",(0,n.jsx)(e.code,{children:\"lib/sample_app.pb.ex\"}),` with several modules within it. If you look\ncarefully, you'll see that the code that was generated is the Elixir representation of the `,(0,n.jsx)(e.code,{children:\"sample_app.proto\"}),` file that\nwe wrote. It contains all of the types that we defined along with the RPC method definitions. With our auto-generated\ncode in place, let's get to work on the actual RPC handlers.`]}),`\n`,(0,n.jsxs)(e.p,{children:[`As previously mentioned, we'll be using an Agent to persist state across gRPC calls instead of a database, for the sake\nof simplicity. Our agent will have the ability to look up users via their ID, and will also be able to create new users.\nCreate a file `,(0,n.jsx)(e.code,{children:\"lib/user_db.ex\"}),\" with the following code which provides that functionality:\"]}),`\n`,(0,n.jsx)(e.figure,{\"data-rehype-pretty-code-figure\":\"\",children:(0,n.jsx)(e.pre,{tabIndex:\"0\",\"data-language\":\"elixir\",\"data-theme\":\"github-dark\",\"data-raw\":`defmodule UserDB do\n use Agent\n\n def start_link(_) do\n Agent.start_link(\n fn -\u003e\n {%{}, 1}\n end,\n name: __MODULE__\n )\n end\n\n def add_user(user) do\n Agent.get_and_update(__MODULE__, fn {users_map, next_id} -\u003e\n updated_users_map = Map.put(users_map, next_id, user)\n\n {Map.put(user, :id, next_id), {updated_users_map, next_id + 1}}\n end)\n end\n\n def get_user(id) do\n Agent.get(__MODULE__, fn {users_map, _next_id} -\u003e\n Map.get(users_map, id)\n end)\n end\nend\n`,children:(0,n.jsxs)(e.code,{\"data-language\":\"elixir\",\"data-theme\":\"github-dark\",style:{display:\"grid\"},children:[(0,n.jsxs)(e.span,{\"data-line\":\"\",children:[(0,n.jsx)(e.span,{style:{color:\"#F97583\"},children:\"defmodule\"}),(0,n.jsx)(e.span,{style:{color:\"#B392F0\"},children:\" UserDB\"}),(0,n.jsx)(e.span,{style:{color:\"#F97583\"},children:\" do\"})]}),`\n`,(0,n.jsxs)(e.span,{\"data-line\":\"\",children:[(0,n.jsx)(e.span,{style:{color:\"#F97583\"},children:\" use\"}),(0,n.jsx)(e.span,{style:{color:\"#B392F0\"},children:\" Agent\"})]}),`\n`,(0,n.jsx)(e.span,{\"data-line\":\"\",children:\" \"}),`\n`,(0,n.jsxs)(e.span,{\"data-line\":\"\",children:[(0,n.jsx)(e.span,{style:{color:\"#F97583\"},children:\" def\"}),(0,n.jsx)(e.span,{style:{color:\"#B392F0\"},children:\" start_link\"}),(0,n.jsx)(e.span,{style:{color:\"#E1E4E8\"},children:\"(\"}),(0,n.jsx)(e.span,{style:{color:\"#6A737D\"},children:\"_\"}),(0,n.jsx)(e.span,{style:{color:\"#E1E4E8\"},children:\") \"}),(0,n.jsx)(e.span,{style:{color:\"#F97583\"},children:\"do\"})]}),`\n`,(0,n.jsxs)(e.span,{\"data-line\":\"\",children:[(0,n.jsx)(e.span,{style:{color:\"#B392F0\"},children:\" Agent\"}),(0,n.jsx)(e.span,{style:{color:\"#E1E4E8\"},children:\".\"}),(0,n.jsx)(e.span,{style:{color:\"#B392F0\"},children:\"start_link\"}),(0,n.jsx)(e.span,{style:{color:\"#E1E4E8\"},children:\"(\"})]}),`\n`,(0,n.jsxs)(e.span,{\"data-line\":\"\",children:[(0,n.jsx)(e.span,{style:{color:\"#F97583\"},children:\" fn\"}),(0,n.jsx)(e.span,{style:{color:\"#F97583\"},children:\" -\u003e\"})]}),`\n`,(0,n.jsxs)(e.span,{\"data-line\":\"\",children:[(0,n.jsx)(e.span,{style:{color:\"#E1E4E8\"},children:\" {%{}, \"}),(0,n.jsx)(e.span,{style:{color:\"#79B8FF\"},children:\"1\"}),(0,n.jsx)(e.span,{style:{color:\"#E1E4E8\"},children:\"}\"})]}),`\n`,(0,n.jsxs)(e.span,{\"data-line\":\"\",children:[(0,n.jsx)(e.span,{style:{color:\"#F97583\"},children:\" end\"}),(0,n.jsx)(e.span,{style:{color:\"#E1E4E8\"},children:\",\"})]}),`\n`,(0,n.jsxs)(e.span,{\"data-line\":\"\",children:[(0,n.jsx)(e.span,{style:{color:\"#79B8FF\"},children:\" name:\"}),(0,n.jsx)(e.span,{style:{color:\"#79B8FF\"},children:\" __MODULE__\"})]}),`\n`,(0,n.jsx)(e.span,{\"data-line\":\"\",children:(0,n.jsx)(e.span,{style:{color:\"#E1E4E8\"},children:\" )\"})}),`\n`,(0,n.jsx)(e.span,{\"data-line\":\"\",children:(0,n.jsx)(e.span,{style:{color:\"#F97583\"},children:\" end\"})}),`\n`,(0,n.jsx)(e.span,{\"data-line\":\"\",children:\" \"}),`\n`,(0,n.jsxs)(e.span,{\"data-line\":\"\",children:[(0,n.jsx)(e.span,{style:{color:\"#F97583\"},children:\" def\"}),(0,n.jsx)(e.span,{style:{color:\"#B392F0\"},children:\" add_user\"}),(0,n.jsx)(e.span,{style:{color:\"#E1E4E8\"},children:\"(user) \"}),(0,n.jsx)(e.span,{style:{color:\"#F97583\"},children:\"do\"})]}),`\n`,(0,n.jsxs)(e.span,{\"data-line\":\"\",children:[(0,n.jsx)(e.span,{style:{color:\"#B392F0\"},children:\" Agent\"}),(0,n.jsx)(e.span,{style:{color:\"#E1E4E8\"},children:\".\"}),(0,n.jsx)(e.span,{style:{color:\"#B392F0\"},children:\"get_and_update\"}),(0,n.jsx)(e.span,{style:{color:\"#E1E4E8\"},children:\"(\"}),(0,n.jsx)(e.span,{style:{color:\"#79B8FF\"},children:\"__MODULE__\"}),(0,n.jsx)(e.span,{style:{color:\"#E1E4E8\"},children:\", \"}),(0,n.jsx)(e.span,{style:{color:\"#F97583\"},children:\"fn\"}),(0,n.jsx)(e.span,{style:{color:\"#E1E4E8\"},children:\" {users_map, next_id} \"}),(0,n.jsx)(e.span,{style:{color:\"#F97583\"},children:\"-\u003e\"})]}),`\n`,(0,n.jsxs)(e.span,{\"data-line\":\"\",children:[(0,n.jsx)(e.span,{style:{color:\"#E1E4E8\"},children:\" updated_users_map \"}),(0,n.jsx)(e.span,{style:{color:\"#F97583\"},children:\"=\"}),(0,n.jsx)(e.span,{style:{color:\"#B392F0\"},children:\" Map\"}),(0,n.jsx)(e.span,{style:{color:\"#E1E4E8\"},children:\".\"}),(0,n.jsx)(e.span,{style:{color:\"#B392F0\"},children:\"put\"}),(0,n.jsx)(e.span,{style:{color:\"#E1E4E8\"},children:\"(users_map, next_id, user)\"})]}),`\n`,(0,n.jsx)(e.span,{\"data-line\":\"\",children:\" \"}),`\n`,(0,n.jsxs)(e.span,{\"data-line\":\"\",children:[(0,n.jsx)(e.span,{style:{color:\"#E1E4E8\"},children:\" {\"}),(0,n.jsx)(e.span,{style:{color:\"#B392F0\"},children:\"Map\"}),(0,n.jsx)(e.span,{style:{color:\"#E1E4E8\"},children:\".\"}),(0,n.jsx)(e.span,{style:{color:\"#B392F0\"},children:\"put\"}),(0,n.jsx)(e.span,{style:{color:\"#E1E4E8\"},children:\"(user, \"}),(0,n.jsx)(e.span,{style:{color:\"#79B8FF\"},children:\":id\"}),(0,n.jsx)(e.span,{style:{color:\"#E1E4E8\"},children:\", next_id), {updated_users_map, next_id \"}),(0,n.jsx)(e.span,{style:{color:\"#F97583\"},children:\"+\"}),(0,n.jsx)(e.span,{style:{color:\"#79B8FF\"},children:\" 1\"}),(0,n.jsx)(e.span,{style:{color:\"#E1E4E8\"},children:\"}}\"})]}),`\n`,(0,n.jsxs)(e.span,{\"data-line\":\"\",children:[(0,n.jsx)(e.span,{style:{color:\"#F97583\"},children:\" end\"}),(0,n.jsx)(e.span,{style:{color:\"#E1E4E8\"},children:\")\"})]}),`\n`,(0,n.jsx)(e.span,{\"data-line\":\"\",children:(0,n.jsx)(e.span,{style:{color:\"#F97583\"},children:\" end\"})}),`\n`,(0,n.jsx)(e.span,{\"data-line\":\"\",children:\" \"}),`\n`,(0,n.jsxs)(e.span,{\"data-line\":\"\",children:[(0,n.jsx)(e.span,{style:{color:\"#F97583\"},children:\" def\"}),(0,n.jsx)(e.span,{style:{color:\"#B392F0\"},children:\" get_user\"}),(0,n.jsx)(e.span,{style:{color:\"#E1E4E8\"},children:\"(id) \"}),(0,n.jsx)(e.span,{style:{color:\"#F97583\"},children:\"do\"})]}),`\n`,(0,n.jsxs)(e.span,{\"data-line\":\"\",children:[(0,n.jsx)(e.span,{style:{color:\"#B392F0\"},children:\" Agent\"}),(0,n.jsx)(e.span,{style:{color:\"#E1E4E8\"},children:\".\"}),(0,n.jsx)(e.span,{style:{color:\"#B392F0\"},children:\"get\"}),(0,n.jsx)(e.span,{style:{color:\"#E1E4E8\"},children:\"(\"}),(0,n.jsx)(e.span,{style:{color:\"#79B8FF\"},children:\"__MODULE__\"}),(0,n.jsx)(e.span,{style:{color:\"#E1E4E8\"},children:\", \"}),(0,n.jsx)(e.span,{style:{color:\"#F97583\"},children:\"fn\"}),(0,n.jsx)(e.span,{style:{color:\"#E1E4E8\"},children:\" {users_map, \"}),(0,n.jsx)(e.span,{style:{color:\"#6A737D\"},children:\"_next_id\"}),(0,n.jsx)(e.span,{style:{color:\"#E1E4E8\"},children:\"} \"}),(0,n.jsx)(e.span,{style:{color:\"#F97583\"},children:\"-\u003e\"})]}),`\n`,(0,n.jsxs)(e.span,{\"data-line\":\"\",children:[(0,n.jsx)(e.span,{style:{color:\"#B392F0\"},children:\" Map\"}),(0,n.jsx)(e.span,{style:{color:\"#E1E4E8\"},children:\".\"}),(0,n.jsx)(e.span,{style:{color:\"#B392F0\"},children:\"get\"}),(0,n.jsx)(e.span,{style:{color:\"#E1E4E8\"},children:\"(users_map, id)\"})]}),`\n`,(0,n.jsxs)(e.span,{\"data-line\":\"\",children:[(0,n.jsx)(e.span,{style:{color:\"#F97583\"},children:\" end\"}),(0,n.jsx)(e.span,{style:{color:\"#E1E4E8\"},children:\")\"})]}),`\n`,(0,n.jsx)(e.span,{\"data-line\":\"\",children:(0,n.jsx)(e.span,{style:{color:\"#F97583\"},children:\" end\"})}),`\n`,(0,n.jsx)(e.span,{\"data-line\":\"\",children:(0,n.jsx)(e.span,{style:{color:\"#F97583\"},children:\"end\"})})]})})}),`\n`,(0,n.jsxs)(e.p,{children:[\"With that in place, we can create our RPC handlers for creating and getting users. Create a file \",(0,n.jsx)(e.code,{children:\"lib/sample_app.ex\"}),`\nwith the following content:`]}),`\n`,(0,n.jsx)(e.figure,{\"data-rehype-pretty-code-figure\":\"\",children:(0,n.jsx)(e.pre,{tabIndex:\"0\",\"data-language\":\"elixir\",\"data-theme\":\"github-dark\",\"data-raw\":`defmodule SampleApp.Endpoint do\n use GRPC.Endpoint\n\n intercept GRPC.Logger.Server\n run SampleApp.User.Server\nend\n\ndefmodule SampleApp.User.Server do\n use GRPC.Server, service: SampleApp.User.Service\n\n def create(request, _stream) do\n new_user =\n UserDB.add_user(%{\n first_name: request.first_name,\n last_name: request.last_name,\n age: request.age\n })\n\n SampleApp.UserReply.new(new_user)\n end\n\n def get(request, _stream) do\n user = UserDB.get_user(request.id)\n\n if user == nil do\n raise GRPC.RPCError, status: :not_found\n else\n SampleApp.UserReply.new(user)\n end\n end\nend\n`,children:(0,n.jsxs)(e.code,{\"data-language\":\"elixir\",\"data-theme\":\"github-dark\",style:{display:\"grid\"},children:[(0,n.jsxs)(e.span,{\"data-line\":\"\",children:[(0,n.jsx)(e.span,{style:{color:\"#F97583\"},children:\"defmodule\"}),(0,n.jsx)(e.span,{style:{color:\"#B392F0\"},children:\" SampleApp\"}),(0,n.jsx)(e.span,{style:{color:\"#E1E4E8\"},children:\".\"}),(0,n.jsx)(e.span,{style:{color:\"#B392F0\"},children:\"Endpoint\"}),(0,n.jsx)(e.span,{style:{color:\"#F97583\"},children:\" do\"})]}),`\n`,(0,n.jsxs)(e.span,{\"data-line\":\"\",children:[(0,n.jsx)(e.span,{style:{color:\"#F97583\"},children:\" use\"}),(0,n.jsx)(e.span,{style:{color:\"#B392F0\"},children:\" GRPC\"}),(0,n.jsx)(e.span,{style:{color:\"#E1E4E8\"},children:\".\"}),(0,n.jsx)(e.span,{style:{color:\"#B392F0\"},children:\"Endpoint\"})]}),`\n`,(0,n.jsx)(e.span,{\"data-line\":\"\",children:\" \"}),`\n`,(0,n.jsxs)(e.span,{\"data-line\":\"\",children:[(0,n.jsx)(e.span,{style:{color:\"#E1E4E8\"},children:\" intercept \"}),(0,n.jsx)(e.span,{style:{color:\"#B392F0\"},children:\"GRPC\"}),(0,n.jsx)(e.span,{style:{color:\"#E1E4E8\"},children:\".\"}),(0,n.jsx)(e.span,{style:{color:\"#B392F0\"},children:\"Logger\"}),(0,n.jsx)(e.span,{style:{color:\"#E1E4E8\"},children:\".\"}),(0,n.jsx)(e.span,{style:{color:\"#B392F0\"},children:\"Server\"})]}),`\n`,(0,n.jsxs)(e.span,{\"data-line\":\"\",children:[(0,n.jsx)(e.span,{style:{color:\"#E1E4E8\"},children:\" run \"}),(0,n.jsx)(e.span,{style:{color:\"#B392F0\"},children:\"SampleApp\"}),(0,n.jsx)(e.span,{style:{color:\"#E1E4E8\"},children:\".\"}),(0,n.jsx)(e.span,{style:{color:\"#B392F0\"},children:\"User\"}),(0,n.jsx)(e.span,{style:{color:\"#E1E4E8\"},children:\".\"}),(0,n.jsx)(e.span,{style:{color:\"#B392F0\"},children:\"Server\"})]}),`\n`,(0,n.jsx)(e.span,{\"data-line\":\"\",children:(0,n.jsx)(e.span,{style:{color:\"#F97583\"},children:\"end\"})}),`\n`,(0,n.jsx)(e.span,{\"data-line\":\"\",children:\" \"}),`\n`,(0,n.jsxs)(e.span,{\"data-line\":\"\",children:[(0,n.jsx)(e.span,{style:{color:\"#F97583\"},children:\"defmodule\"}),(0,n.jsx)(e.span,{style:{color:\"#B392F0\"},children:\" SampleApp\"}),(0,n.jsx)(e.span,{style:{color:\"#E1E4E8\"},children:\".\"}),(0,n.jsx)(e.span,{style:{color:\"#B392F0\"},children:\"User\"}),(0,n.jsx)(e.span,{style:{color:\"#E1E4E8\"},children:\".\"}),(0,n.jsx)(e.span,{style:{color:\"#B392F0\"},children:\"Server\"}),(0,n.jsx)(e.span,{style:{color:\"#F97583\"},children:\" do\"})]}),`\n`,(0,n.jsxs)(e.span,{\"data-line\":\"\",children:[(0,n.jsx)(e.span,{style:{color:\"#F97583\"},children:\" use\"}),(0,n.jsx)(e.span,{style:{color:\"#B392F0\"},children:\" GRPC\"}),(0,n.jsx)(e.span,{style:{color:\"#E1E4E8\"},children:\".\"}),(0,n.jsx)(e.span,{style:{color:\"#B392F0\"},children:\"Server\"}),(0,n.jsx)(e.span,{style:{color:\"#E1E4E8\"},children:\", \"}),(0,n.jsx)(e.span,{style:{color:\"#79B8FF\"},children:\"service:\"}),(0,n.jsx)(e.span,{style:{color:\"#B392F0\"},children:\" SampleApp\"}),(0,n.jsx)(e.span,{style:{color:\"#E1E4E8\"},children:\".\"}),(0,n.jsx)(e.span,{style:{color:\"#B392F0\"},children:\"User\"}),(0,n.jsx)(e.span,{style:{color:\"#E1E4E8\"},children:\".\"}),(0,n.jsx)(e.span,{style:{color:\"#B392F0\"},children:\"Service\"})]}),`\n`,(0,n.jsx)(e.span,{\"data-line\":\"\",children:\" \"}),`\n`,(0,n.jsxs)(e.span,{\"data-line\":\"\",children:[(0,n.jsx)(e.span,{style:{color:\"#F97583\"},children:\" def\"}),(0,n.jsx)(e.span,{style:{color:\"#B392F0\"},children:\" create\"}),(0,n.jsx)(e.span,{style:{color:\"#E1E4E8\"},children:\"(request, \"}),(0,n.jsx)(e.span,{style:{color:\"#6A737D\"},children:\"_stream\"}),(0,n.jsx)(e.span,{style:{color:\"#E1E4E8\"},children:\") \"}),(0,n.jsx)(e.span,{style:{color:\"#F97583\"},children:\"do\"})]}),`\n`,(0,n.jsxs)(e.span,{\"data-line\":\"\",children:[(0,n.jsx)(e.span,{style:{color:\"#E1E4E8\"},children:\" new_user \"}),(0,n.jsx)(e.span,{style:{color:\"#F97583\"},children:\"=\"})]}),`\n`,(0,n.jsxs)(e.span,{\"data-line\":\"\",children:[(0,n.jsx)(e.span,{style:{color:\"#B392F0\"},children:\" UserDB\"}),(0,n.jsx)(e.span,{style:{color:\"#E1E4E8\"},children:\".\"}),(0,n.jsx)(e.span,{style:{color:\"#B392F0\"},children:\"add_user\"}),(0,n.jsx)(e.span,{style:{color:\"#E1E4E8\"},children:\"(%{\"})]}),`\n`,(0,n.jsxs)(e.span,{\"data-line\":\"\",children:[(0,n.jsx)(e.span,{style:{color:\"#79B8FF\"},children:\" first_name:\"}),(0,n.jsx)(e.span,{style:{color:\"#E1E4E8\"},children:\" request.first_name,\"})]}),`\n`,(0,n.jsxs)(e.span,{\"data-line\":\"\",children:[(0,n.jsx)(e.span,{style:{color:\"#79B8FF\"},children:\" last_name:\"}),(0,n.jsx)(e.span,{style:{color:\"#E1E4E8\"},children:\" request.last_name,\"})]}),`\n`,(0,n.jsxs)(e.span,{\"data-line\":\"\",children:[(0,n.jsx)(e.span,{style:{color:\"#79B8FF\"},children:\" age:\"}),(0,n.jsx)(e.span,{style:{color:\"#E1E4E8\"},children:\" request.age\"})]}),`\n`,(0,n.jsx)(e.span,{\"data-line\":\"\",children:(0,n.jsx)(e.span,{style:{color:\"#E1E4E8\"},children:\" })\"})}),`\n`,(0,n.jsx)(e.span,{\"data-line\":\"\",children:\" \"}),`\n`,(0,n.jsxs)(e.span,{\"data-line\":\"\",children:[(0,n.jsx)(e.span,{style:{color:\"#B392F0\"},children:\" SampleApp\"}),(0,n.jsx)(e.span,{style:{color:\"#E1E4E8\"},children:\".\"}),(0,n.jsx)(e.span,{style:{color:\"#B392F0\"},children:\"UserReply\"}),(0,n.jsx)(e.span,{style:{color:\"#E1E4E8\"},children:\".\"}),(0,n.jsx)(e.span,{style:{color:\"#B392F0\"},children:\"new\"}),(0,n.jsx)(e.span,{style:{color:\"#E1E4E8\"},children:\"(new_user)\"})]}),`\n`,(0,n.jsx)(e.span,{\"data-line\":\"\",children:(0,n.jsx)(e.span,{style:{color:\"#F97583\"},children:\" end\"})}),`\n`,(0,n.jsx)(e.span,{\"data-line\":\"\",children:\" \"}),`\n`,(0,n.jsxs)(e.span,{\"data-line\":\"\",children:[(0,n.jsx)(e.span,{style:{color:\"#F97583\"},children:\" def\"}),(0,n.jsx)(e.span,{style:{color:\"#B392F0\"},children:\" get\"}),(0,n.jsx)(e.span,{style:{color:\"#E1E4E8\"},children:\"(request, \"}),(0,n.jsx)(e.span,{style:{color:\"#6A737D\"},children:\"_stream\"}),(0,n.jsx)(e.span,{style:{color:\"#E1E4E8\"},children:\") \"}),(0,n.jsx)(e.span,{style:{color:\"#F97583\"},children:\"do\"})]}),`\n`,(0,n.jsxs)(e.span,{\"data-line\":\"\",children:[(0,n.jsx)(e.span,{style:{color:\"#E1E4E8\"},children:\" user \"}),(0,n.jsx)(e.span,{style:{color:\"#F97583\"},children:\"=\"}),(0,n.jsx)(e.span,{style:{color:\"#B392F0\"},children:\" UserDB\"}),(0,n.jsx)(e.span,{style:{color:\"#E1E4E8\"},children:\".\"}),(0,n.jsx)(e.span,{style:{color:\"#B392F0\"},children:\"get_user\"}),(0,n.jsx)(e.span,{style:{color:\"#E1E4E8\"},children:\"(request.id)\"})]}),`\n`,(0,n.jsx)(e.span,{\"data-line\":\"\",children:\" \"}),`\n`,(0,n.jsxs)(e.span,{\"data-line\":\"\",children:[(0,n.jsx)(e.span,{style:{color:\"#F97583\"},children:\" if\"}),(0,n.jsx)(e.span,{style:{color:\"#E1E4E8\"},children:\" user \"}),(0,n.jsx)(e.span,{style:{color:\"#F97583\"},children:\"==\"}),(0,n.jsx)(e.span,{style:{color:\"#79B8FF\"},children:\" nil\"}),(0,n.jsx)(e.span,{style:{color:\"#F97583\"},children:\" do\"})]}),`\n`,(0,n.jsxs)(e.span,{\"data-line\":\"\",children:[(0,n.jsx)(e.span,{style:{color:\"#F97583\"},children:\" raise\"}),(0,n.jsx)(e.span,{style:{color:\"#B392F0\"},children:\" GRPC\"}),(0,n.jsx)(e.span,{style:{color:\"#E1E4E8\"},children:\".\"}),(0,n.jsx)(e.span,{style:{color:\"#B392F0\"},children:\"RPCError\"}),(0,n.jsx)(e.span,{style:{color:\"#E1E4E8\"},children:\", \"}),(0,n.jsx)(e.span,{style:{color:\"#79B8FF\"},children:\"status:\"}),(0,n.jsx)(e.span,{style:{color:\"#79B8FF\"},children:\" :not_found\"})]}),`\n`,(0,n.jsx)(e.span,{\"data-line\":\"\",children:(0,n.jsx)(e.span,{style:{color:\"#F97583\"},children:\" else\"})}),`\n`,(0,n.jsxs)(e.span,{\"data-line\":\"\",children:[(0,n.jsx)(e.span,{style:{color:\"#B392F0\"},children:\" SampleApp\"}),(0,n.jsx)(e.span,{style:{color:\"#E1E4E8\"},children:\".\"}),(0,n.jsx)(e.span,{style:{color:\"#B392F0\"},children:\"UserReply\"}),(0,n.jsx)(e.span,{style:{color:\"#E1E4E8\"},children:\".\"}),(0,n.jsx)(e.span,{style:{color:\"#B392F0\"},children:\"new\"}),(0,n.jsx)(e.span,{style:{color:\"#E1E4E8\"},children:\"(user)\"})]}),`\n`,(0,n.jsx)(e.span,{\"data-line\":\"\",children:(0,n.jsx)(e.span,{style:{color:\"#F97583\"},children:\" end\"})}),`\n`,(0,n.jsx)(e.span,{\"data-line\":\"\",children:(0,n.jsx)(e.span,{style:{color:\"#F97583\"},children:\" end\"})}),`\n`,(0,n.jsx)(e.span,{\"data-line\":\"\",children:(0,n.jsx)(e.span,{style:{color:\"#F97583\"},children:\"end\"})})]})})}),`\n`,(0,n.jsxs)(e.p,{children:[\"Our file defines two modules. The \",(0,n.jsx)(e.code,{children:\"SampleApp.Endpoint\"}),` module defines the gRPC server and provides the\nhandler module to service requests. The `,(0,n.jsx)(e.code,{children:\"SampleApp.User.Server\"}),` module contains the actual implementations of the two\nRPC calls that we defined. You'll notice that for each of the handlers, we provide the correct return type (as defined in\nour Protocol Buffer file). When we encounter an error (in this case, looking up a user that doesn't exist), we raise a\n`,(0,n.jsx)(e.code,{children:\"GRPC.RPCError\"}),\" with the appropriate status code.\"]}),`\n`,(0,n.jsxs)(e.p,{children:[`All that is left now is to start up our Agent and our gRPC server, and we're good to go. Open up\n`,(0,n.jsx)(e.code,{children:\"lib/sample_app/application.ex\"}),\" and ensure that your process \",(0,n.jsx)(e.code,{children:\"children\"}),\" list looks like this:\"]}),`\n`,(0,n.jsx)(e.figure,{\"data-rehype-pretty-code-figure\":\"\",children:(0,n.jsx)(e.pre,{tabIndex:\"0\",\"data-language\":\"elixir\",\"data-theme\":\"github-dark\",\"data-raw\":`children = [\n UserDB,\n {GRPC.Server.Supervisor, {SampleApp.Endpoint, 50051}}\n]\n`,children:(0,n.jsxs)(e.code,{\"data-language\":\"elixir\",\"data-theme\":\"github-dark\",style:{display:\"grid\"},children:[(0,n.jsxs)(e.span,{\"data-line\":\"\",children:[(0,n.jsx)(e.span,{style:{color:\"#E1E4E8\"},children:\"children \"}),(0,n.jsx)(e.span,{style:{color:\"#F97583\"},children:\"=\"}),(0,n.jsx)(e.span,{style:{color:\"#E1E4E8\"},children:\" [\"})]}),`\n`,(0,n.jsxs)(e.span,{\"data-line\":\"\",children:[(0,n.jsx)(e.span,{style:{color:\"#B392F0\"},children:\" UserDB\"}),(0,n.jsx)(e.span,{style:{color:\"#E1E4E8\"},children:\",\"})]}),`\n`,(0,n.jsxs)(e.span,{\"data-line\":\"\",children:[(0,n.jsx)(e.span,{style:{color:\"#E1E4E8\"},children:\" {\"}),(0,n.jsx)(e.span,{style:{color:\"#B392F0\"},children:\"GRPC\"}),(0,n.jsx)(e.span,{style:{color:\"#E1E4E8\"},children:\".\"}),(0,n.jsx)(e.span,{style:{color:\"#B392F0\"},children:\"Server\"}),(0,n.jsx)(e.span,{style:{color:\"#E1E4E8\"},children:\".\"}),(0,n.jsx)(e.span,{style:{color:\"#B392F0\"},children:\"Supervisor\"}),(0,n.jsx)(e.span,{style:{color:\"#E1E4E8\"},children:\", {\"}),(0,n.jsx)(e.span,{style:{color:\"#B392F0\"},children:\"SampleApp\"}),(0,n.jsx)(e.span,{style:{color:\"#E1E4E8\"},children:\".\"}),(0,n.jsx)(e.span,{style:{color:\"#B392F0\"},children:\"Endpoint\"}),(0,n.jsx)(e.span,{style:{color:\"#E1E4E8\"},children:\", \"}),(0,n.jsx)(e.span,{style:{color:\"#79B8FF\"},children:\"50051\"}),(0,n.jsx)(e.span,{style:{color:\"#E1E4E8\"},children:\"}}\"})]}),`\n`,(0,n.jsx)(e.span,{\"data-line\":\"\",children:(0,n.jsx)(e.span,{style:{color:\"#E1E4E8\"},children:\"]\"})})]})})}),`\n`,(0,n.jsxs)(e.p,{children:[\"With that in place, you should be able to run \",(0,n.jsx)(e.code,{children:\"mix grpc.server\"}),` from the terminal to start your gRPC server. In another\nterminal session (and from within the project directory), you should be able to use `,(0,n.jsx)(e.code,{children:\"grpcurl\"}),` commands to interact with\nyour application:`]}),`\n`,(0,n.jsx)(e.figure,{\"data-rehype-pretty-code-figure\":\"\",children:(0,n.jsx)(e.pre,{tabIndex:\"0\",\"data-language\":\"shell\",\"data-theme\":\"github-dark\",\"data-raw\":`$ grpcurl -plaintext -proto sample_app.proto -d '{\"first_name\": \"Bob\", \"last_name\": \"Smith\", \"age\": 40}' localhost:50051 sample_app.User.Create\n{\n \"id\": 1,\n \"firstName\": \"Bob\",\n \"lastName\": \"Smith\",\n \"age\": 40\n}\n\n$ grpcurl -plaintext -proto sample_app.proto -d '{\"id\": 1}' localhost:50051 sample_app.User.Get\n{\n \"firstName\": \"Bob\",\n \"lastName\": \"Smith\",\n \"age\": 40\n}\n\n$ grpcurl -plaintext -proto sample_app.proto -d '{\"id\": 2}' localhost:50051 sample_app.User.Get\nERROR:\n Code: NotFound\n Message: Some requested entity (e.g., file or directory) was not found\n`,children:(0,n.jsxs)(e.code,{\"data-language\":\"shell\",\"data-theme\":\"github-dark\",style:{display:\"grid\"},children:[(0,n.jsxs)(e.span,{\"data-line\":\"\",children:[(0,n.jsx)(e.span,{style:{color:\"#B392F0\"},children:\"$\"}),(0,n.jsx)(e.span,{style:{color:\"#9ECBFF\"},children:\" grpcurl\"}),(0,n.jsx)(e.span,{style:{color:\"#79B8FF\"},children:\" -plaintext\"}),(0,n.jsx)(e.span,{style:{color:\"#79B8FF\"},children:\" -proto\"}),(0,n.jsx)(e.span,{style:{color:\"#9ECBFF\"},children:\" sample_app.proto\"}),(0,n.jsx)(e.span,{style:{color:\"#79B8FF\"},children:\" -d\"}),(0,n.jsx)(e.span,{style:{color:\"#9ECBFF\"},children:` '{\"first_name\": \"Bob\", \"last_name\": \"Smith\", \"age\": 40}'`}),(0,n.jsx)(e.span,{style:{color:\"#9ECBFF\"},children:\" localhost:50051\"}),(0,n.jsx)(e.span,{style:{color:\"#9ECBFF\"},children:\" sample_app.User.Create\"})]}),`\n`,(0,n.jsx)(e.span,{\"data-line\":\"\",children:(0,n.jsx)(e.span,{style:{color:\"#E1E4E8\"},children:\"{\"})}),`\n`,(0,n.jsxs)(e.span,{\"data-line\":\"\",children:[(0,n.jsx)(e.span,{style:{color:\"#B392F0\"},children:' \"id\"'}),(0,n.jsx)(e.span,{style:{color:\"#79B8FF\"},children:\":\"}),(0,n.jsx)(e.span,{style:{color:\"#9ECBFF\"},children:\" 1,\"})]}),`\n`,(0,n.jsxs)(e.span,{\"data-line\":\"\",children:[(0,n.jsx)(e.span,{style:{color:\"#B392F0\"},children:' \"firstName\"'}),(0,n.jsx)(e.span,{style:{color:\"#79B8FF\"},children:\":\"}),(0,n.jsx)(e.span,{style:{color:\"#9ECBFF\"},children:' \"Bob\",'})]}),`\n`,(0,n.jsxs)(e.span,{\"data-line\":\"\",children:[(0,n.jsx)(e.span,{style:{color:\"#B392F0\"},children:' \"lastName\"'}),(0,n.jsx)(e.span,{style:{color:\"#79B8FF\"},children:\":\"}),(0,n.jsx)(e.span,{style:{color:\"#9ECBFF\"},children:' \"Smith\",'})]}),`\n`,(0,n.jsxs)(e.span,{\"data-line\":\"\",children:[(0,n.jsx)(e.span,{style:{color:\"#B392F0\"},children:' \"age\"'}),(0,n.jsx)(e.span,{style:{color:\"#79B8FF\"},children:\":\"}),(0,n.jsx)(e.span,{style:{color:\"#79B8FF\"},children:\" 40\"})]}),`\n`,(0,n.jsx)(e.span,{\"data-line\":\"\",children:(0,n.jsx)(e.span,{style:{color:\"#E1E4E8\"},children:\"}\"})}),`\n`,(0,n.jsx)(e.span,{\"data-line\":\"\",children:\" \"}),`\n`,(0,n.jsxs)(e.span,{\"data-line\":\"\",children:[(0,n.jsx)(e.span,{style:{color:\"#B392F0\"},children:\"$\"}),(0,n.jsx)(e.span,{style:{color:\"#9ECBFF\"},children:\" grpcurl\"}),(0,n.jsx)(e.span,{style:{color:\"#79B8FF\"},children:\" -plaintext\"}),(0,n.jsx)(e.span,{style:{color:\"#79B8FF\"},children:\" -proto\"}),(0,n.jsx)(e.span,{style:{color:\"#9ECBFF\"},children:\" sample_app.proto\"}),(0,n.jsx)(e.span,{style:{color:\"#79B8FF\"},children:\" -d\"}),(0,n.jsx)(e.span,{style:{color:\"#9ECBFF\"},children:` '{\"id\": 1}'`}),(0,n.jsx)(e.span,{style:{color:\"#9ECBFF\"},children:\" localhost:50051\"}),(0,n.jsx)(e.span,{style:{color:\"#9ECBFF\"},children:\" sample_app.User.Get\"})]}),`\n`,(0,n.jsx)(e.span,{\"data-line\":\"\",children:(0,n.jsx)(e.span,{style:{color:\"#E1E4E8\"},children:\"{\"})}),`\n`,(0,n.jsxs)(e.span,{\"data-line\":\"\",children:[(0,n.jsx)(e.span,{style:{color:\"#B392F0\"},children:' \"firstName\"'}),(0,n.jsx)(e.span,{style:{color:\"#79B8FF\"},children:\":\"}),(0,n.jsx)(e.span,{style:{color:\"#9ECBFF\"},children:' \"Bob\",'})]}),`\n`,(0,n.jsxs)(e.span,{\"data-line\":\"\",children:[(0,n.jsx)(e.span,{style:{color:\"#B392F0\"},children:' \"lastName\"'}),(0,n.jsx)(e.span,{style:{color:\"#79B8FF\"},children:\":\"}),(0,n.jsx)(e.span,{style:{color:\"#9ECBFF\"},children:' \"Smith\",'})]}),`\n`,(0,n.jsxs)(e.span,{\"data-line\":\"\",children:[(0,n.jsx)(e.span,{style:{color:\"#B392F0\"},children:' \"age\"'}),(0,n.jsx)(e.span,{style:{color:\"#79B8FF\"},children:\":\"}),(0,n.jsx)(e.span,{style:{color:\"#79B8FF\"},children:\" 40\"})]}),`\n`,(0,n.jsx)(e.span,{\"data-line\":\"\",children:(0,n.jsx)(e.span,{style:{color:\"#E1E4E8\"},children:\"}\"})}),`\n`,(0,n.jsx)(e.span,{\"data-line\":\"\",children:\" \"}),`\n`,(0,n.jsxs)(e.span,{\"data-line\":\"\",children:[(0,n.jsx)(e.span,{style:{color:\"#B392F0\"},children:\"$\"}),(0,n.jsx)(e.span,{style:{color:\"#9ECBFF\"},children:\" grpcurl\"}),(0,n.jsx)(e.span,{style:{color:\"#79B8FF\"},children:\" -plaintext\"}),(0,n.jsx)(e.span,{style:{color:\"#79B8FF\"},children:\" -proto\"}),(0,n.jsx)(e.span,{style:{color:\"#9ECBFF\"},children:\" sample_app.proto\"}),(0,n.jsx)(e.span,{style:{color:\"#79B8FF\"},children:\" -d\"}),(0,n.jsx)(e.span,{style:{color:\"#9ECBFF\"},children:` '{\"id\": 2}'`}),(0,n.jsx)(e.span,{style:{color:\"#9ECBFF\"},children:\" localhost:50051\"}),(0,n.jsx)(e.span,{style:{color:\"#9ECBFF\"},children:\" sample_app.User.Get\"})]}),`\n`,(0,n.jsx)(e.span,{\"data-line\":\"\",children:(0,n.jsx)(e.span,{style:{color:\"#B392F0\"},children:\"ERROR:\"})}),`\n`,(0,n.jsxs)(e.span,{\"data-line\":\"\",children:[(0,n.jsx)(e.span,{style:{color:\"#B392F0\"},children:\" Code:\"}),(0,n.jsx)(e.span,{style:{color:\"#9ECBFF\"},children:\" NotFound\"})]}),`\n`,(0,n.jsxs)(e.span,{\"data-line\":\"\",children:[(0,n.jsx)(e.span,{style:{color:\"#B392F0\"},children:\" Message:\"}),(0,n.jsx)(e.span,{style:{color:\"#9ECBFF\"},children:\" Some\"}),(0,n.jsx)(e.span,{style:{color:\"#9ECBFF\"},children:\" requested\"}),(0,n.jsx)(e.span,{style:{color:\"#9ECBFF\"},children:\" entity\"}),(0,n.jsx)(e.span,{style:{color:\"#E1E4E8\"},children:\" (e.g., \"}),(0,n.jsx)(e.span,{style:{color:\"#9ECBFF\"},children:\"file\"}),(0,n.jsx)(e.span,{style:{color:\"#9ECBFF\"},children:\" or\"}),(0,n.jsx)(e.span,{style:{color:\"#9ECBFF\"},children:\" directory\"}),(0,n.jsx)(e.span,{style:{color:\"#E1E4E8\"},children:\") was not found\"})]})]})})}),`\n`,(0,n.jsx)(e.h2,{id:\"conclusion\",children:\"Conclusion\"}),`\n`,(0,n.jsx)(e.p,{children:`Thanks for sticking with me to the end. Hopefully, you learned a thing or two about gRPC and how to go about\nusing it within an Elixir application. If you would like to learn more about gRPC or any of the tools that I mentioned,\nI suggest going through the following resources:`}),`\n`,(0,n.jsxs)(e.ul,{children:[`\n`,(0,n.jsx)(e.li,{children:(0,n.jsx)(e.a,{href:\"https://github.com/elixir-grpc/grpc\",children:\"elixir-grpc\"})}),`\n`,(0,n.jsx)(e.li,{children:(0,n.jsx)(e.a,{href:\"https://github.com/tony612/protobuf-elixir\",children:\"protobuf-elixir\"})}),`\n`,(0,n.jsx)(e.li,{children:(0,n.jsx)(e.a,{href:\"https://github.com/akoutmos/sample_app\",children:\"Our sample app\"})}),`\n`]}),`\n`,(0,n.jsx)(e.p,{children:(0,n.jsxs)(e.strong,{children:[\"P.S. If you'd like to read Elixir Alchemy posts as soon as they get off the press, \",(0,n.jsx)(e.a,{href:\"/elixir-alchemy\",children:\"subscribe to our Elixir Alchemy newsletter and never miss a single post\"}),\"!\"]})})]})}function w(l={}){let{wrapper:e}=l.components||{};return e?(0,n.jsx)(e,Object.assign({},l,{children:(0,n.jsx)(d,l)})):d(l)}var b=w;function _(l,e){throw new Error(\"Expected \"+(e?\"component\":\"object\")+\" `\"+l+\"` to be defined: you likely forgot to import, pass, or provide it.\")}return f(C);})();\n;return Component;"},"_id":"posts/2020-03-24-how-to-use-grpc-in-elixir.html.mdx","_raw":{"sourceFilePath":"posts/2020-03-24-how-to-use-grpc-in-elixir.html.mdx","sourceFileName":"2020-03-24-how-to-use-grpc-in-elixir.html.mdx","sourceFileDir":"posts","contentType":"mdx","flattenedPath":"posts/2020-03-24-how-to-use-grpc-in-elixir.html"},"type":"Post","year":"2020","serieSlug":null,"sitemapSlug":"2020/03/24/how-to-use-grpc-in-elixir.html"},"posts":{"favorite":[],"related":[{"date":"2024-12-17T00:00:00+00:00","slug":"2024/12/17/appsignals-top-5-elixir-posts-in-2024","title":"AppSignal’s Top 5 Elixir Posts in 2024"},{"date":"2024-12-10T00:00:00+00:00","slug":"2024/12/10/distributed-phoenix-deployment-and-scaling","title":"Distributed Phoenix: Deployment and Scaling"},{"date":"2024-11-12T00:00:00+00:00","slug":"2024/11/12/how-to-track-errors-in-oban-for-elixir-using-appsignal","title":"How to Track Errors in Oban for Elixir Using AppSignal"},{"date":"2024-10-29T00:00:00+00:00","slug":"2024/10/29/managing-distributed-state-with-genservers-in-phoenix-and-elixir","title":"Managing Distributed State with GenServers in Phoenix and Elixir"},{"date":"2024-10-08T00:00:00+00:00","slug":"2024/10/08/find-and-fix-n-plus-1-queries-using-appsignal-for-a-phoenix-app-in-elixir","title":"Find and Fix N+1 Queries Using AppSignal for a Phoenix App in Elixir"},{"date":"2024-09-17T00:00:00+00:00","slug":"2024/09/17/a-complete-guide-to-phoenix-for-elixir-monitoring-with-appsignal","title":"A Complete Guide to Phoenix for Elixir Monitoring with AppSignal"},{"date":"2024-09-03T00:00:00+00:00","slug":"2024/09/03/scaling-your-phoenix-app-in-elixir-with-flame","title":"Scaling Your Phoenix App in Elixir with FLAME"},{"date":"2024-08-20T00:00:00+00:00","slug":"2024/08/20/livestate-for-elixir-an-overview-and-how-to-build-embeddable-web-apps","title":"LiveState for Elixir: An Overview and How to Build Embeddable Web Apps"},{"date":"2024-08-06T00:00:00+00:00","slug":"2024/08/06/custom-instrumentation-for-a-phoenix-app-in-elixir-with-appsignal","title":"Custom Instrumentation for a Phoenix App in Elixir with AppSignal"},{"date":"2024-07-23T00:00:00+00:00","slug":"2024/07/23/enhancing-your-elixir-codebase-with-gleam","title":"Enhancing Your Elixir Codebase with Gleam"}]}},"__N_SSG":true},"page":"/[year]/[month]/[day]/[text]","query":{"year":"2020","month":"03","day":"24","text":"how-to-use-grpc-in-elixir"},"buildId":"4VD4GTi1vs_NVN2Wl-Jyb","isFallback":false,"dynamicIds":[9142,7572,3653,4396],"gsp":true,"scriptLoader":[]}</script></body></html>

Pages: 1 2 3 4 5 6 7 8 9 10