One place for hosting & domains

      Custom

      Building Custom APIs with Strapi


      Let’s build a custom API. Strapi is a Node CMS that lets us create our own APIs quickly from a clean dashboard. We don’t have to jump into Node code to create a database, database connections, models, etc. Strapi lets us create an API in very little time!

      Key takeaways

      • Building a custom API can be done quickly
      • Building a custom API without writing Node code
      • We can get up and running with a REST API or GraphQL API

      Join us if…

      • You want to build your own APIs, and you want to do it quickly!
      • You want to build a backend API, but don’t have the Node expertise, or want to focus on frontends.

      Prerequisites
      Knowledge of APIs and what APIs are is good to have.

      Hosted by
      Chris Sevilleja (@chrisoncode) is the founder of scotch.io and Senior Developer Advocate at DigitalOcean. He loves trying to figure out the most efficient and practical way to build apps that we can ship to our customers.

      Thursday, June 11
      1:00 p.m. ET



      Source link

      How To Create Custom Components in React


      The author selected Creative Commons to receive a donation as part of the Write for DOnations program.

      Introduction

      In this tutorial, you’ll learn to create custom components in React. Components are independent pieces of functionality that you can reuse in your application, and are the building blocks of all React applications. Often, they can be simple JavaScript functions and classes, but you use them as if they were customized HTML elements. Buttons, menus, and any other front-end page content can all be created as components. Components can also contain state information and display markdown.

      After learning how to create components in React, you’ll be able to split complex applications into small pieces that are easier to build and maintain.

      In this tutorial, you’ll create a list of emojis that will display their names on click. The emojis will be built using a custom component and will be called from inside another custom component. By the end of this tutorial, you’ll have made custom components using both JavaScript classes and JavaScript functions, and you’ll understand how to separate existing code into reusable pieces and how to store the components in a readable file structure.

      Prerequisites

      Step 1 — Setting Up the React Project

      In this step, you’ll create a base for your project using Create React App. You will also modify the default project to create your base project by mapping over a list of emojis and adding a small amount of styling.

      First, create a new project. Open a terminal, then run the following command:

      • npx create-react-app tutorial-03-component

      Once this is finished, change into the project directory:

      Open the App.js code in a text editor:

      Next, take out the template code created by Create React App, then replace the contents with new React code that displays a list of emojis:

      tutorial-03-component/src/App.js

      import React from 'react';
      import './App.css';
      
      const displayEmojiName = event => alert(event.target.id);
      const emojis = [
        {
          emoji: '😀',
          name: "test grinning face"
        },
        {
          emoji: '🎉',
          name: "party popper"
        },
        {
          emoji: '💃',
          name: "woman dancing"
        }
      ];
      
      function App() {
        const greeting = "greeting";
        const displayAction = false;
        return(
          <div className="container">
            <h1 id={greeting}>Hello, World</h1>
            {displayAction && <p>I am writing JSX</p>}
            <ul>
              {
                emojis.map(emoji => (
                  <li key={emoji.name}>
                    <button
                      onClick={displayEmojiName}
                    >
                      <span role="img" aria-label={emoji.name} id={emoji.name}>{emoji.emoji}</span>
                    </button>
                  </li>
                ))
              }
            </ul>
          </div>
        )
      }
      
      export default App;
      

      This code uses JSX syntax to map() over the emojis array and list them as <li> list items. It also attaches onClick events to display emoji data in the browser. To explore the code in more detail, check out How to Create React Elements with JSX, which contains a detailed explanation of the JSX.

      Save and close the file. You can now delete the logo.svg file, since it was part of the template and you are not referencing it anymore:

      Now, update the styling. Open src/App.css:

      Replace the contents with the following CSS to center the elements and adjust the font:

      tutorial-03-component/src/App.css

      .container {
          display: flex;
          flex-direction: column;
          align-items: center;
      }
      
      button {
          font-size: 2em;
          border: 0;
          padding: 0;
          background: none;
          cursor: pointer;
      }
      
      ul {
          display: flex;
          padding: 0;
      }
      
      li {
          margin: 0 20px;
          list-style: none;
          padding: 0;
      }
      

      This uses flex to center the main <h1> and list elements. It also removes default button styles and <li> styles so the emojis line up in a row. More details can be found at How to Create React Elements with JSX.

      Save and exit the file.

      Open another terminal window in the root of your project. Start the project with the following command:

      After the command runs, you’ll see the project running in your web browser at http://localhost:3000.

      Leave this running the entire time you work on your project. Every time you save the project, the browser will auto-refresh and show the most up-to-date code.

      You will see your project page with Hello, World and the three emojis that you listed in your App.js file:

      Browser with emoji

      Now that you’ve set up your code, you can now start putting together components in React.

      Step 2 — Creating an Independent Component with React Classes

      Now that you have your project running, you can start making your custom component. In this step, you’ll create an independent React component by extending the base React Component class. You’ll create a new class, add methods, and use the render function to show data.

      React components are self-contained elements that you can reuse throughout a page. By making small, focused pieces of code, you can move and reuse pieces as your application grows. The key here is that they are self-contained and focused, allowing you to separate out code into logical pieces. In fact, you have already been working with logically separated components: The App.js file is a functional component, one that you will see more of in Step 3.

      There are two types of custom component: class-based and functional. The first component you are going to make is a class-based component. You will make a new component called Instructions that explains the instructions for the emoji viewer.

      Note: Class-based components used to be the most popular way of creating React components. But with the introduction of React Hooks, many developers and libraries are shifting to using functional components.

      Though functional components are now the norm, you will often find class components in legacy code. You don’t need to use them, but you do need to know how to recognize them. They also give a clear introduction to many future concepts, such as state management. In this tutorial, you’ll learn to make both class and functional components.

      To start, create a new file. By convention, component files are capitalized:

      • touch src/Instructions.js

      Then open the file in your text editor:

      First, import React and the Component class and export Instructions with the following lines:

      tutorial-03-component/src/Instructions.js

      import React, { Component } from 'react';
      
      export default class Instructions extends Component {}
      

      Importing React will convert the JSX. Component is a base class that you’ll extend to create your component. To extend that, you created a class that has the name of your component (Instructions) and extended the base Component with the export line. You’re also exporting this class as the default with export default keywords at the start of the class declaration.

      The class name should be capitalized and should match the name of the file. This is important when using debugging tools, which will display the name of the component. If the name matches the file structure, it will be easier to locate the relevant component.

      The base Component class has several methods you can use in your custom class. The most important method, and the only one you’ll use in this tutorial, is the render() method. The render() method returns the JSX code that you want to display in the browser.

      To start, add a little explanation of the app in a <p> tag:

      tutorial-03-component/src/Instructions.js

      import React, { Component } from 'react';
      
      export class Instructions extends Component {
      
        render() {
          return(
            <p>Click on an emoji to view the emoji short name.</p>
          )
        }
      
      }
      

      Save and close the file. At this point, there’s still no change to your browser. That’s because you haven’t used the new component yet. To use the component, you’ll have to add it into another component that connects to the root component. In this project, <App> is the root component in index.js. To make it appear in your application, you’ll need to add to the <App> component.

      Open src/App.js in a text editor:

      First, you’ll need to import the component:

      tutorial-03-component/src/App.js

      import React from 'react';
      
      import Instructions from './Instructions';
      
      import './App.css';
      
      ...
      
      export default App;
      

      Since it’s the default import, you could import to any name you wanted. It’s best to keep the names consistent for readability—the import should match the component name, which should match the file name—but the only firm rule is that the component must start with a capital letter. That’s how React knows it’s a React component.

      Now that you’ve imported the component, add it to the rest of your code as if it were a custom HTML element:

      tutorial-03-component/src/App.js

      import React from 'react';
      
      import Instructions from './Instructions.js'
      
      ...
      function App() {
        const greeting = "greeting";
        const displayAction = false;
        return(
          <div className="container">
            <h1 id={greeting}>Hello, World</h1>
            {displayAction && <p>I am writing JSX</p>}
            <Instructions />
            <ul>
              {
                emojis.map(emoji => (
                  <li key={emoji.name}>
                    <button
                      onClick={displayEmojiName}
                    >
                      <span role="img" aria-label={emoji.name} id={emoji.name}>{emoji.emoji}</span>
                    </button>
                  </li>
                ))
              }
            </ul>
          </div>
        )
      }
      
      export default App;
      

      In this code, you wrapped the component with angle brackets. Since this component doesn’t have any children, it can be self closing by ending with />.

      Save the file. When you do, the page will refresh and you’ll see the new component.

      Browser with instruction text

      Now that you have some text, you can add an image. Download an emoji image from wikimedia and save it in the src directory as emoji.svg with the following command:

      • curl -o src/emoji.svg https://upload.wikimedia.org/wikipedia/commons/3/33/Twemoji_1f602.svg

      curl makes the request to the URL, and the -o flag allows you to save the file as src/emoji.svg.

      Next, open your component file:

      Import the emoji and add it to your custom component with a dynamic link:

      tutorial-03-component/src/Instructions.js

      import React, { Component } from 'react';
      import emoji from './emoji.svg'
      
      export default class Instructions extends Component {
      
        render() {
          return(
            <>
              <img alt="laughing crying emoji" src={emoji} />
              <p>Click on an emoji to view the emoji short name.</p>
            </>
          )
        }
      }
      

      Notice that you need to include the file extension .svg when importing. When you import, you are importing a dynamic path that is created by webpack when the code compiles. For more information, refer to How To Set Up a React Project with Create React App.

      You also need to wrap the <img> and <p> tags with empty tags to ensure that you are returning a single element.

      Save the file. When you reload, the image will be very large compared to the rest of the content:

      Browser window with large emoji image

      To make the image smaller, you’ll need to add some CSS and a className to your custom component.

      First, in Instructions.js, change the empty tags to a div and give it a className of instructions:

      tutorial-03-component/src/Instructions.js

      import React, { Component } from 'react';
      import emoji from './emoji.svg'
      
      export default class Instructions extends Component {
      
        render() {
          return(
            <div className="instructions">
              <img alt="laughing crying emoji" src={emoji} />
              <p>Click on an emoji to view the emoji short name.</p>
            </div>
          )
        }
      }
      

      Save and close the file. Next open App.css:

      Create rules for the .instructions class selector:

      tutorial-03-component/src/App.css

      .container {
          display: flex;
          flex-direction: column;
          align-items: center;
      }
      
      ...
      
      .instructions {
          display: flex;
          flex-direction: column;
          align-items: center;
      }
      

      When you add a display of flex styling, you make the img and the p centered with flexbox. You changed the direction so that everything lines up vertically with flex-direction: column;. The line align-items: center; will center the elements on the screen.

      Now that your elements are lined up, you need to change the image size. Give the img inside the div a width and height of 100px.

      tutorial-03-component/src/App.css

      .container {
          display: flex;
          flex-direction: column;
          align-items: center;
      }
      
      ...
      
      .instructions {
          display: flex;
          flex-direction: column;
          align-items: center;
      }
      
      .instructions img {
          width: 100px;
          height: 100px;
      }
      

      Save and close the file. The browser will reload and you’ll see the image is much smaller:

      Browser window with smaller image

      At this point, you’ve created an independent and reusable custom component. To see how it’s reusable, add a second instance to App.js.

      Open App.js:

      In App.js, add a second instance of the component:

      tutorial-03-component/src/App.js

      import React from 'react';
      
      import Instructions from './Instructions.js'
      
      ...
      
      function App() {
        const greeting = "greeting";
        const displayAction = false;
        return(
          <div className="container">
            <h1 id={greeting}>Hello, World</h1>
            {displayAction && <p>I am writing JSX</p>}
            <Instructions />
            <Instructions />
            <ul>
              {
                emojis.map(emoji => (
                  <li key={emoji.name}>
                    <button
                      onClick={displayEmojiName}
                    >
                      <span role="img" aria-label={emoji.name} id={emoji.name}>{emoji.emoji}</span>
                    </button>
                  </li>
                ))
              }
            </ul>
          </div>
        )
      }
      
      export default App;
      

      Save the file. When the browser reloads, you’ll see the component twice.

      Browser with two instances of the Instructions component

      In this case, you wouldn’t want two instances of Instructions, but you can see that the component can be efficiently reused. When you create custom buttons or tables, you will likely use them multiple times on one page, making them perfect for custom components.

      For now, you can delete the extra image tag. In your text editor, delete the second <Instructions /> and save the file:

      tutorial-03-component/src/App.js

      import React from 'react';
      
      import Instructions from './Instructions.js'
      
      ...
      
      function App() {
        const greeting = "greeting";
        const displayAction = false;
        return(
          <div className="container">
            <h1 id={greeting}>Hello, World</h1>
            {displayAction && <p>I am writing JSX</p>}
            <Instructions />
            <ul>
              {
                emojis.map(emoji => (
                  <li key={emoji.name}>
                    <button
                      onClick={displayEmojiName}
                    >
                      <span role="img" aria-label={emoji.name} id={emoji.name}>{emoji.emoji}</span>
                    </button>
                  </li>
                ))
              }
            </ul>
          </div>
        )
      }
      
      export default App;
      

      Now you have a reusable, independent component that you can add to a parent component multiple times. The structure you have now works for a small number of components, but there is a slight problem. All of the files are mixed together. The image for <Instructions> is in the same directory as the assets for <App>. You also are mixing the CSS code for <App> with the CSS for <Instructions>.

      In the next step, you’ll create a file structure that will give each component independence by grouping their functionality, styles, and dependencies together, giving you the ability to move them around as you need.

      Step 3 — Creating a Readable File Structure

      In this step, you’ll create a file structure to organize your components and their assets, such as images, CSS, and other JavaScript files. You’ll be grouping code by component, not by asset type. In other words, you won’t have a separate directory for CSS, images, and JavaScript. Instead you’ll have a separate directory for each component that will contain the relevant CSS, JavaScript, and images. In both cases, you are separating concerns.

      Since you have an independent component, you need a file structure that groups the relevant code. Currently, everything is in the same directory. List out the items in your src directory:

      The output will show that things are getting pretty cluttered:

      Output

      App.css Instructions.js index.js App.js emoji.svg serviceWorker.js App.test.js index.css setupTests.js

      You have code for the <App> component (App.css, App.js, and App.test.js) sitting alongside your root component (index.css and index.js) and your custom component Instructions.js.

      React is intentionally agnostic about file structure. It does not recommend a particular structure, and the project can work with a variety of different file hierarchies. But we recommend to add some order to avoid overloading your root directory with components, CSS files, and images that will be difficult to navigate. Also, explicit naming can make it easier to see which pieces of your project are related. For example, an image file named Logo.svg may not clearly be part of a component called Header.js.

      One of the simplest structures is to create a components directory with a separate directory for each component. This will allow you to group your components separately from your configuration code, such as serviceWorker, while grouping the assets with the components.

      Creating a Components Directory

      To start, create a directory called components:

      Next, move the following components and code into the directory: App.css, App.js, App.test.js, Instructions.js, and emoji.svg:

      • mv src/App.* src/components/
      • mv src/Instructions.js src/components/
      • mv src/emoji.svg src/components/

      Here, you are using a wildcard (*) to select all files that start with App..

      After you move the code, you’ll see an error in your terminal running npm start.

      Output

      Failed to compile. ./src/App.js Error: ENOENT: no such file or directory, open 'your_file_path/tutorial-03-component/src/App.js'

      Remember, all of the code is importing using relative paths. If you change the path for some files, you’ll need to update the code.

      To do that, open index.js.

      Then change the path of the App import to import from the components/
      directory.

      tutorial-03-component/src/index.js

      import React from 'react';
      import ReactDOM from 'react-dom';
      import './index.css';
      import App from './components/App';
      import * as serviceWorker from './serviceWorker';
      
      ...
      
      serviceWorker.unregister();
      

      Save and close the file. Your script will detect the changes and the error will disappear.

      Now you have components in a separate directory. As your applications become more complex, you may have directories for API services, data stores, and utility functions. Separating component code is the first step, but you still have CSS code for Instructions mixed in the App.css file. To create this logical separation, you will first move the components into separate directories.

      Moving Components to Individual Directories

      First, make a directory specifically for the <App> component:

      Then move the related files into the new directory:

      • mv src/components/App.* src/components/App

      When you do you’ll get a similar error to the last section:

      Output

      Failed to compile. ./src/components/App.js Error: ENOENT: no such file or directory, open 'your_file_path/tutorial-03-component/src/components/App.js'

      In this case, you’ll need to update two things. First, you’ll need to update the path in index.js.

      Open the index.js file:

      Then update the import path for App to point to the App component in the App directory.

      tutorial-03-component/src/index.js

      import React from 'react';
      import ReactDOM from 'react-dom';
      import './index.css';
      import App from './components/App/App';
      import * as serviceWorker from './serviceWorker';
      
      ...
      
      serviceWorker.unregister();
      

      Save and close the file. The application still won’t run. You’ll see an error like this:

      Output

      Failed to compile. ./src/components/App/App.js Module not found: Can't resolve './Instructions.js' in 'your_file_path/tutorial-03-component/src/components/App'

      Since <Instructions> is not on the same directory level as the <App> component, you’ll need to change the import path. Before that, create a directory for Instructions. Make a directory called Instructions in the src/components directory:

      • mkdir src/components/Instructions

      Then move Instructions.js and emoji.svg into that directory:

      • mv src/components/Instructions.js src/components/Instructions
      • mv src/components/emoji.svg src/components/Instructions

      Now that the Instructions component directory has been created, you can finish updating the file paths to connect your component to your app.

      Updating import Paths

      Now that components are in individual directories, you can adjust the import path in App.js.

      Open App.js:

      • nano src/components/App/App.js

      Since the path is relative, you’ll need to move up one directory—src/components—then into the Instructions directory for Instructions.js, but since this is a JavaScript file, you don’t need the final import.

      tutorial-03-component/src/components/App/App.js

      import React from 'react';
      
      import Instructions from '../Instructions/Instructions.js';
      
      import './App.css';
      
      ...
      
      export default App;
      

      Save and close the file. Now that your imports are all using the correct path, you’re browser will update and show the application.

      Browser window with smaller image

      Note: You can also call the root file in each directory index.js. For example, instead of src/components/App/App.js you could create src/components/App/index.js. The advantage to this is that your imports are slightly smaller. If the path points to a directory, the import will look for an index.js file. The import for src/components/App/index.js in the src/index.js file would be import ./components/App. The disadvantage of this approach is that you have a lot of files with the same name, which can make it difficult to read in some text editors. Ultimately, it’s a personal and team decision, but it’s best to be consistent.

      Separating Code in Shared Files

      Now each component has its own directory, but not everything is fully independent. The last step is to extract the CSS for Instructions to a separate file.

      First, create a CSS file in src/components/Instructions:

      • touch src/components/Instructions/Instructions.css

      Next, open the CSS file in your text editor:

      • nano src/components/Instructions/Instructions.css

      Add in the instructions CSS that you created in an earlier section:

      tutorial-03-component/src/components/Instructions/Instructions.css

      .instructions {
          display: flex;
          flex-direction: column;
          align-items: center;
      }
      
      .instructions img {
          width: 100px;
          height: 100px;
      }
      

      Save and close the file. Next, remove the instructions CSS from src/components/App/App.css.

      • nano src/components/App/App.css

      Remove the lines about .instructions. The final file will look like this:

      tutorial-03-component/src/components/App/App.css

      .container {
          display: flex;
          flex-direction: column;
          align-items: center;
      }
      
      button {
          font-size: 2em;
          border: 0;
          padding: 0;
          background: none;
          cursor: pointer;
      }
      
      ul {
          display: flex;
          padding: 0;
      }
      
      li {
          margin: 0 20px;
          list-style: none;
          padding: 0;
      }
      

      Save and close the file. Finally, import the CSS in Instructions.js:

      • nano src/components/Instructions/Instructions.js

      Import the CSS using the relative path:

      tutorial-03-component/src/components/Instructions/Instructions.js

      import React, { Component } from 'react';
      import './Instructions.css';
      import emoji from './emoji.svg'
      
      export default class Instructions extends Component {
      
        render() {
          return(
            <div className="instructions">
              <img alt="laughing crying emoji" src={emoji} />
              <p>Click on an emoji to view the emoji short name.</p>
            </div>
          )
        }
      }
      

      Save and close the file. Your browser window will look as it did before, except now all the file assets are grouped in the same directory.

      Browser window with smaller image

      Now, take a final look at the structure. First, the src/ directory:

      You have the root component index.js and the related CSS index.css next to the components/ directory and utility files such as serviceWorker.js and setupTests.js:

      Output

      components serviceWorker.js index.css setupTests.js index.js

      Next, look inside components:

      You’ll see a directory for each component:

      Output

      App Instructions

      If you look inside each component, you’ll see the component code, CSS, test, and image files if they exist.

      Output

      App.css App.js App.test.js
      • ls src/components/Instructions

      Output

      Instructions.css Instructions.js emoji.svg

      At this point, you’ve created a solid structure for your project. You moved a lot of code around, but now that you have a structure, it will scale easier.

      This is not the only way to compose your structure. Some file structures can take advantage of code splitting by specifying a directory that will be split into different packages. Other file structures split by route and use a common directory for components that are used across routes.

      For now, stick with a less complex approach. As a need for another structure emerges, it’s always easier to move from simple to complex. Starting with a complex structure before you need it will make refactoring difficult.

      Now that you have created and organized a class-based component, in the next step you’ll create a functional component.

      Step 4 — Building a Functional Component

      In this step, you’ll create a functional component. Functional components are the most common component in contemporary React code. These components tend to be shorter, and unlike class-based components, they can use React hooks, a new form of state and event management.

      A functional component is a JavaScript function that returns some JSX. It doesn’t need to extend anything and there are no special methods to memorize.

      To refactor <Instructions> as a functional component, you need to change the class to a function and remove the render method so that you are left with only the return statement.

      To do that, first open Instructions.js in a text editor.

      • nano src/components/Instructions/Instructions.js

      Change the class declaration to a function declaration:

      tutorial-03-component/src/components/Instructions/Instructions.js

      import React, { Component } from 'react';
      import './Instructions.css';
      import emoji from './emoji.svg'
      
      export default function Instructions() {
        render() {
          return(
            <div className="instructions">
              <img alt="laughing crying emoji" src={emoji} />
              <p>Click on an emoji to view the emoji short name.</p>
            </div>
          )
        }
      }
      

      Next, remove the import of { Component }:

      tutorial-03-component/src/components/Instructions/Instructions.js

      import React from 'react';
      import './Instructions.css';
      import emoji from './emoji.svg'
      
      export default function Instructions() {
      
        render() {
          return(
            <div className="instructions">
              <img alt="laughing crying emoji" src={emoji} />
              <p>Click on an emoji to view the emoji short name.</p>
            </div>
          )
        }
      }
      

      Finally, remove the render() method. At that point, you are only returning JSX.

      tutorial-03-component/src/components/Instructions/Instructions.js

      import React from 'react';
      import './Instructions.css';
      import emoji from './emoji.svg'
      
      export default function Instructions() {
        return(
          <div className="instructions">
              <img alt="laughing crying emoji" src={emoji} />
              <p>Click on an emoji to view the emoji short name.</p>
          </div>
        )
      }
      

      Save the file. The browser will refresh and you’ll see your page as it was before.

      Browser with emoji

      You could also rewrite the function as an arrow function using the implicit return. The main difference is that you lose the function body. You will also need to first assign the function to a variable and then export the variable:

      tutorial-03-component/src/components/Instructions/Instructions.js

      import React from 'react';
      import './Instructions.css';
      import emoji from './emoji.svg'
      
      const Instructions = () => (
        <div className="instructions">
          <img alt="laughing crying emoji" src={emoji} />
          <p>Click on an emoji to view the emoji short name.</p>
        </div>
      )
      
      export default Instructions;
      

      Simple functional components and class-based components are very similar. When you have a simple component that doesn’t store state, it’s best to use a functional component. The real difference between the two is how you store a component’s state and use properties. Class-based components use methods and properties to set state and tend to be a little longer. Functional components use hooks to store state or manage changes and tend to be a little shorter.

      Conclusion

      Now you have a small application with independent pieces. You created two major types of components: functional and class. You separated out parts of the components into directories so that you could keep similar pieces of code grouped together. You also imported and reused the components.

      With an understanding of components, you can start to look at your applications as pieces that you can take apart and put back together. Projects become modular and interchangable. The ability to see whole applications as a series of components is an important step in thinking in React. If you would like to look at more React tutorials, take a look at our React Topic page, or return to the How To Code in React.js series page.



      Source link

      Creating Custom Errors in Go


      Introduction

      Go provides two methods to create errors in the standard library, errors.New and fmt.Errorf. When communicating more complicated error information to your users, or to your future self when debugging, sometimes these two mechanisms are not enough to adequately capture and report what has happened. To convey this more complex error information and attain more functionality, we can implement the standard library interface type, error.

      The syntax for this would be as follows:

      type error interface {
        Error() string
      }
      

      The builtin package defines error as an interface with a single Error() method that returns an error message as a string. By implementing this method, we can transform any type we define into an error of our own.

      Let’s try running the following example to see an implementation of the error interface:

      package main
      
      import (
          "fmt"
          "os"
      )
      
      type MyError struct{}
      
      func (m *MyError) Error() string {
          return "boom"
      }
      
      func sayHello() (string, error) {
          return "", &MyError{}
      }
      
      func main() {
          s, err := sayHello()
          if err != nil {
              fmt.Println("unexpected error: err:", err)
              os.Exit(1)
          }
          fmt.Println("The string:", s)
      }
      

      We’ll see the following output:

      Output

      unexpected error: err: boom exit status 1

      Here we’ve created a new empty struct type, MyError, and defined the Error() method on it. The Error() method returns the string "boom".

      Within main(), we call the function sayHello that returns an empty string and a new instance of MyError. Since sayHello will always return an error, the fmt.Println invocation within the body of the if statement in main() will always execute. We then use fmt.Println to print the short prefix string "unexpected error:" along with the instance of MyError held within the err variable.

      Notice that we don’t have to directly call Error(), since the fmt package is able to automatically detect that this is an implementation of error. It calls Error() transparently to get the string "boom" and concatenates it with the prefix string "unexpected error: err:".

      Collecting Detailed Information in a Custom Error

      Sometimes a custom error is the cleanest way to capture detailed error information. For example, let’s say we want to capture the status code for errors produced by an HTTP request; run the following program to see an implementation of error that allows us to cleanly capture that information:

      package main
      
      import (
          "errors"
          "fmt"
          "os"
      )
      
      type RequestError struct {
          StatusCode int
      
          Err error
      }
      
      func (r *RequestError) Error() string {
          return fmt.Sprintf("status %d: err %v", r.StatusCode, r.Err)
      }
      
      func doRequest() error {
          return &RequestError{
              StatusCode: 503,
              Err:        errors.New("unavailable"),
          }
      }
      
      func main() {
          err := doRequest()
          if err != nil {
              fmt.Println(err)
              os.Exit(1)
          }
          fmt.Println("success!")
      }
      

      We will see the following output:

      Output

      status 503: err unavailable exit status 1

      In this example, we create a new instance of RequestError and provide the status code and an error using the errors.New function from the standard library. We then print this using fmt.Println as in previous examples.

      Within the Error() method of RequestError, we use the fmt.Sprintf function to construct a string using the information provided when the error was created.

      Type Assertions and Custom Errors

      The error interface exposes only one method, but we may need to access the other methods of error implementations to handle an error properly. For example, we may have several custom implementations of error that are temporary and can be retried—denoted by the presence of a Temporary() method.

      Interfaces provide a narrow view into the wider set of methods provided by types, so we must use a type assertion to change the methods that view is displaying, or to remove it entirely.

      The following example augments the RequestError shown previously to have a Temporary() method which will indicate whether or not callers should retry the request:

      package main
      
      import (
          "errors"
          "fmt"
          "net/http"
          "os"
      )
      
      type RequestError struct {
          StatusCode int
      
          Err error
      }
      
      func (r *RequestError) Error() string {
          return r.Err.Error()
      }
      
      func (r *RequestError) Temporary() bool {
          return r.StatusCode == http.StatusServiceUnavailable // 503
      }
      
      func doRequest() error {
          return &RequestError{
              StatusCode: 503,
              Err:        errors.New("unavailable"),
          }
      }
      
      func main() {
          err := doRequest()
          if err != nil {
              fmt.Println(err)
              re, ok := err.(*RequestError)
              if ok {
                  if re.Temporary() {
                      fmt.Println("This request can be tried again")
                  } else {
                      fmt.Println("This request cannot be tried again")
                  }
              }
              os.Exit(1)
          }
      
          fmt.Println("success!")
      }
      

      We will see the following output:

      Output

      unavailable This request can be tried again exit status 1

      Within main(), we call doRequest() which returns an error interface to us. We first print the error message returned by the Error() method. Next, we attempt to expose all methods from RequestError by using the type assertion re, ok := err.(*RequestError). If the type assertion succeeded, we then use the Temporary() method to see if this error is a temporary error. Since the StatusCode set by doRequest() is 503, which matches http.StatusServiceUnavailable, this returns true and causes "This request can be tried again" to be printed. In practice, we would instead make another request rather than printing a message.

      Wrapping Errors

      Commonly, an error will be generated from something outside of your program such as: a database, a network connection, etc. The error messages provided from these errors don’t help anyone find the origin of the error. Wrapping errors with extra information at the beginning of an error message would provide some needed context for successful debugging.

      The following example demonstrates how we can attach some contextual information to an otherwise cryptic error returned from some other function:

      package main
      
      import (
          "errors"
          "fmt"
      )
      
      type WrappedError struct {
          Context string
          Err     error
      }
      
      func (w *WrappedError) Error() string {
          return fmt.Sprintf("%s: %v", w.Context, w.Err)
      }
      
      func Wrap(err error, info string) *WrappedError {
          return &WrappedError{
              Context: info,
              Err:     err,
          }
      }
      
      func main() {
          err := errors.New("boom!")
          err = Wrap(err, "main")
      
          fmt.Println(err)
      }
      

      We will see the following output:

      Output

      main: boom!

      WrappedError is a struct with two fields: a context message as a string, and an error that this WrappedError is providing more information about. When the Error() method is invoked, we again use fmt.Sprintf to print the context message, then the error (fmt.Sprintf knows to implicitly call the Error() method as well).

      Within main(), we create an error using errors.New, and then we wrap that error using the Wrap function we defined. This allows us to indicate that this error was generated in "main". Also, since our WrappedError is also an error, we could wrap other WrappedErrors—this would allow us to see a chain to help us track down the source of the error. With a little help from the standard library, we can even embed complete stack traces in our errors.

      Conclusion

      Since the error interface is only a single method, we’ve seen that we have great flexibility in providing different types of errors for different situations. This can encompass everything from communicating multiple pieces of information as part of an error all the way to implementing exponential backoff. While the error handling mechanisms in Go might on the surface seem simplistic, we can achieve quite rich handling using these custom errors to handle both common and uncommon situations.

      Go has another mechanism to communicate unexpected behavior, panics. In our next article in the error handling series, we will examine panics—what they are and how to handle them.



      Source link