portada proyecto meteoApp

Proyecto MeteoApp | Parte #1

脷ltima actualizaci贸n:

驴Qu茅 vamos a construir?

Vamos a construir a una app para buscar la predicci贸n meteorol贸gica en cualquier ciudad del mundo, usando la API de AccuWeather. Aqu铆 puedes ver la versi贸n final. 馃尀

Requisitos y consideraciones previas 

Para poder seguir este proyecto sin perderte debes tener conocimientos de:

Estructura inicial de archivos

Vamos a trabajar con un archivo HTML, un archivo CSS y varios archivos JS (JavaScript). El objetivo de tener varios archivos JS es tener nuestro c贸digo m谩s organizado y dividido. As铆, nuestro proyecto tendr谩 la siguiente estructura:

  • 馃搨JS
    • 馃摑app.js 鉃 gestionar谩 lo relacionado con DOM manipulation
    • 馃摑forecast.js 鉃 gestionar谩 el c贸digo responsable de interactuar con la API de predicci贸n meteorol贸gica
  • 馃摑index.html
  • 馃摑styles.css

1. Creamos el boilerplate de nuestro index.html. A帽adimos el CDN  de bootstrap y a continuaci贸n, un link a nuestro propio archivo CSS. Vinculamos el archivo con nuestros archivos JS, primero forecast.js y segundo, app.js. El orden de los archivos JS importa, ya que el c贸digo que escribiremos en app.js depender谩 del c贸digo escrito en forecast.js, por eso es fundamental que 茅ste vaya primero.

<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
    <link rel="stylesheet" href="styles.css">
    <title>MeteoApp</title>
</head>
<body>
    
    <script src="JS/forecast.js"></script>
    <script src="JS/app.js"></script>
</body>
</html>

2. Creamos el contenido de nuestra app en el index.html. Vamos a utilizar bootstrap para casi todas las clases que a帽adamos, s贸lo vamos a crear un par de clases de CSS propias.

La template de nuestra app va a consistir b谩sicamente en una especie de tarjeta que muestra el tiempo de una ciudad X, con una imagen, un icono y una descripci贸n. Tambi茅n contendr谩 un peque帽o formulario para que el usuario busque la predicci贸n meteorol贸gica de una ciudad.

La imagen ser谩 diferente dependiendo de si es de d铆a o de noche para reflejar esa circunstancia. Por el momento vamos a usar un placeholder, pero despu茅s inyectaremos las im谩genes din谩micamente con JS. Para el placeholder usaremos la web placeholder.com.

<body>
    <div class="container my-5 mx-auto">
        <h1 class="text-muted text-center my-4">
            Meteo App
        </h1>
        <form class="change-location my-4 text-center text-muted">
            <label for="city">Enter a city for weather info</label>
            <input type="text" name="city" class="form-control p-4">
        </form>
        <div class="card shadow rounded">
            <img src="https://via.placeholder.com/400x300" class="time card-img-top">
            <div class="icon bg-light mx-auto text-center">
                <!-- icon will go here -->
            </div>
            <div class="text-muted text-uppercase text-center details">
                <h5 class="my-3">City name</h5>
                <div class="my-3">Weather condition</div>
                <div class="display-4 my-4">
                    <span>temp</span>
                    <span>&deg;C</span>
                </div>
            </div>
        </div>
    </div>
    <script src="JS/forecast.js"></script>
    <script src="JS/app.js"></script>
</body>

3. En el archivo styles.css hacemos una peque帽a configuraci贸n para mejorar el aspecto de nuestra app.

body {
    background: #efecd8;
    letter-spacing: 0.2em;
}

.container {
    max-width: 400px;
}

Este es su aspecto actual:

meteoApp estado inicial

Primeros pasos con la API de AccuWeather

 Vamos a sentar las bases para interactuar con esta API, extraer los datos que necesitamos y finalmente mostrarlos en nuestra web. Las webs que ofrecen APIs normalmente tienen una URL dedicada a este fin. Este es el caso para AccuWeather (鈥URL para developers). 

1. Debemos crear una cuenta gratuita para poder usar sus APIs. La opci贸n "register" est谩 en la navbar superior. Te mandar谩n un email para que entres en su web y crees una contrase帽a. 

2. La mayor铆a de APIs nos solicitan que creemos una especie de app a trav茅s de la cual vamos a utilizar esa API. Es como tener una estanter铆a e ir a帽adiendo libros. 馃摎 Cada libro ser铆a una "app" de AccuWeather destinado a un fin. As铆, al crear una app, AccuWeather le asocia un c贸digo (una API key) y nos lo proporciona. 

Para crear una app, vamos a "my apps" y le damos a "add a new app". Contestamos a las preguntas que nos hacen: weatherApp, desktop website, worldwide, JavaScript, etc. A continuaci贸n nos saldr谩 que nos han aprobado dicha app. Si entramos en ella, podremos ver la API key que nos han creado. 

La API key estar谩 englobada en nuestra http request, de manera que cuando hagamos esa request, estaremos enviando tambi茅n nuestra API key. As铆, AccuWeather sabe qu茅 usuario est谩 haciendo la request a efectos de controlar cu谩ntas requests hace un usuario. 馃暤锔忊嶁檪锔

Como estamos en una versi贸n gratuita, existen restricciones sobre el n煤mero de requests que podemos hacer al d铆a. Adem谩s, s贸lo podremos crear una app por cuenta. Si por cualquier motivo nos pasamos de las requests diarias, s贸lo tenemos que darle a "Delete Meteo App" y crear una app nueva. Nos dar谩 una nueva API key que tendremos que sustituir por la que ya hab铆amos escrito en nuestro c贸digo. 馃槗 Pero ea, es un mal menor. 

3. Copiamos la API key y la pegamos en el archivo forecast.js, dentro de una variable a la que llamamos key

const key = 'aqu铆 tu API key';

Actualmente tenemos una restricci贸n de 50 requests diarias. As铆 que cuidado si tienes la funci贸n de auto-guardado en tu IDE. 馃

En cuanto a la informaci贸n que vamos a solicitar de esta API, debemos solicitarla en dos partes. 

  • 1陋: request a un endpoint para obtener informaci贸n sobre una ciudad X (el c贸digo de la ciudad).
  • 2陋: request a un endpoint distinto utilizando el c贸digo de la ciudad (city code) para obtener las condiciones meteorol贸gicas asociadas a esa ciudad.

Los endpoints los podemos encontrar en la pesta帽a "API REFERENCE", como es lo habitual en una API. Ah铆 dentro encontramos los endpoints clasificados por categor铆as. Nosotros necesitamos la categor铆a de "locations API" para obtener la informaci贸n de la ciudad en primer lugar. 

4. Hacemos click en "locations API" y localizamos la opci贸n "city search". Ah铆 est谩 el primer endpoint que vamos a usar. Recordemos que ese endpoint nos devolver谩 un city code.

5. Volvemos a las categor铆as y seleccionamos "current conditions API". Ah铆 vamos a utilizar el endpoint de "current conditions" para hacer nuestra segunda request. 

http://dataservice.accuweather.com/currentconditions/v1/{locationKey}

Ese locationKey es en realidad el city code que habremos obtenido previamente. Habiendo hecho esta introducci贸n, vamos a desarrollar los puntos 4 y 5 en profundidad. 馃挭

1陋 request: Llamando a la city search API

Sabemos que la primera request que tenemos que hacer es a la locations API para obtener informaci贸n sobre una ciudad concreta, por ejemplo Zurich. Cuando hagamos esa request, el navegador nos devolver谩 un conjunto de informaci贸n entre la que est谩 un location key code (a lo que antes nos hemos referido como city code). 

Ese location key code es un identificador 煤nico de cada ciudad. 

1. El endpoint que queremos es el que nos da la opci贸n de "city search". Si pinchamos ah铆, podemos hacer una simulaci贸n de una request. Ver谩s que en ning煤n sitio pone "endpoint". 馃 Para que no nos hagamos un l铆o, ten en cuenta esto:

base + queries = endpoint

La base es lo que tambi茅n se conoce como resource URL. Se llama "base" porque es la parte inicial de un endpoint. Para construir un verdadero endpoint, debemos adjuntarle queries. Esto es, par谩metros de nuestra b煤squeda.

Por ejemplo, para este caso vamos a necesitar dos par谩metros:

  • apikey
  • q 鉃 la ciudad sobre la que queramos informaci贸n

2. Hacemos el test introduciendo dichos par谩metros y d谩ndole al bot贸n "Send this request".

test api accuweather

Eso nos deber铆a devolver una serie de datos como esos de la imagen. A nosotros lo que nos interesa es la informaci贸n de la propiedad Keyporque ese es el identificador de la ciudad que necesitaremos para hacer la segunda request. 

馃拋鈥嶁檧锔廝asemos a la acci贸n y hagamos esta request desde nuestro c贸digo, desde el forecast.js. 

3. Creamos una funci贸n as铆ncrona que se encargar谩 de hacer la request. La llamamos getCity. Para que sea as铆ncrona, le a帽adimos la keyword async. Creamos una variable dentro de la funci贸n llamada base, que contendr谩 la resource URL de la que habl谩bamos antes. 

En cuanto a los par谩metros, sabemos que necesitamos dos (apikey q). La apikey ya la tenemos, as铆 que nos creamos una variable llamada query que almacenar谩 ambos par谩metrosUn par谩metro de una query se construye siempre empezando con un signo de interrogaci贸n "?". 

Sintaxis de una query:

const query = `?nombrePrimerParam=valorPrimerParam&nombreSegundoParam=valorSegundoParam`;

Recuerda que el nombre exacto de los par谩metros nos los da AccuWeather. 馃檵鈥嶁檧锔 Ojito con esto, porque el nombre debe ser exacto, as铆 que cuidado con las may煤sculas/min煤sculas. 

query parameters accuweather

Creamos el contenido de la variable query entre template stringsporque as铆 es m谩s sencillo incluir nuestra variable key. 

4. El segundo par谩metro (q) corresponde a la ciudad. No vamos a escribir el nombre de la ciudad en nuestro c贸digo porque eso har铆a que fuese est谩tico y har铆amos siempre la request sobre la misma ciudad. En lugar de eso, le pasamos un argumento a la funci贸n getCity. Lo llamamos city. 

As铆, cuando invoquemos a la funci贸n getCity, le pasaremos la ciudad como par谩metro.

5. Ya tenemos nuestra base y nuestra query. Ahora tenemos que fusionarlos para crear un endpoint. Para eso, creamos una variable a la que llamamos response y utilizamos la fetch API para crear una promesa y la keyword await para esperar a que la promesa se resuelva. A la fetch API le pasamos la suma de base + query, as铆 JS podr谩 fusionarlos en uno solo string. 馃槍

6. Convertimos la respuesta que obtendremos en JSON data, usando el m茅todo json(). Almacenamos el resultado en una variable llamada data, que tambi茅n devolver谩 una promesa

Hacemos un console.log de la data para que ver qu茅 nos devuelve el navegador.  馃憖

7. Invocamos a la funci贸n getCity, pas谩ndole la ciudad que queramos. 

const getCity = async (city) => {

    const base = 'http://dataservice.accuweather.com/locations/v1/cities/search';
    const query = `?apikey=${key}&q=${city}`;

    const response = await fetch(base + query);
    const data = await response.json();

    console.log(data);
};

getCity('Zurich');

隆Y voil脿! 馃憦 En tu consola deber铆a aparecer un array con varios objetos. Todos se refieren a Zurich, pero est谩n en orden de m谩s a menos exactitud. Si desplegamos el primero, veremos que se refiere a Zurich, Suiza. Ese es el que necesitamos.

8. As铆 que lo seleccionamos en nuestro c贸digo. Y en lugar de hacer un console.log, hacemos un return de data. 

9. Dado que la funci贸n getCity devuelve una promesa, especificamos qu茅 hacer cuando la promesa se resuelva (usando el m茅todo then()), o sea rechazada (usando el m茅todo catch()). 

const getCity = async (city) => {

    const base = 'http://dataservice.accuweather.com/locations/v1/cities/search';
    const query = `?apikey=${key}&q=${city}`;

    const response = await fetch(base + query);
    const data = await response.json();

    return data[0]
};

getCity('Zurich')
    .then(data => console.log(data))
    .catch(err => console.log(err));

Ahora en nuestra consola s贸lo deber铆a aparecer la informaci贸n de Zurich, Suiza, incluyendo la propiedad Key. 

2陋 request: Llamando a la current conditions鈥嬧 API

1. Desde nuestra cuenta de AccuWeather, entramos a la secci贸n Current Conditions API y localizamos la opci贸n "Current Conditions" (deber铆a ser la primera). Si hacemos click, entramos en una interface muy similar al proceso de la primera request. Se nos dan ciertas instrucciones y la base URL, igual que antes. 

F铆jate que la base URL contiene ya el city code (el c贸digo 煤nico de cada ciudad).

http://dataservice.accuweather.com/currentconditions/v1/{locationKey}

As铆 que, como ver谩s en la interface, locationKey no forma parte de los Query Parameters

馃 Las llaves son solo para indicarnos que estamos ante una variable, pero no debemos usarlas cuando hagamos nuestra request. Como ves en los Query Parameters, el 煤nico query que necesitamos es apikey. As铆 que volvemos a nuestro forecast.js y hacemos la request desde ah铆.

2. Creamos una segunda funci贸n antes de getCity, a la que llamamos getWeatherque ser谩 la responsable de gestionar esta segunda request para obtener las condiciones meteorol贸gicas de una ciudad X. Esta funci贸n es as铆ncrona, igual que getCity. 

// get weather info
const getWeather = async () => {

};

A esa funci贸n debemos pasarle por par谩metro la propiedad Keyque es el city code, recuerda. A ese valor tenemos acceso desde el m茅todo then(), a trav茅s del par谩metro data.

getCity('Zurich')
    .then(data => console.log(data))
    .catch(err => console.log(err));

Al par谩metro que le pasamos a la funci贸n getWeather lo llamamos idpara no hacernos un l铆o entre tanto key, apikey, Key, etc. 馃樀

3. Creamos una variable dentro de la funci贸n a la que llamamos base (como en la primera request). Esta variable almacena la resource URL que nos da AccuWeather. Hemos visto que esa URL no est谩 completa hasta que le pasemos una locationKey, y eso podemos hacerlo a帽adiendo el par谩metro id al final.

Para eso tendremos que definir nuestra variable con template strings

const getWeather = async (id) => {

    const base = `http://dataservice.accuweather.com/currentconditions/v1/${id}`;
};

4. Creamos una variable llamada query que almacene los par谩metros de la request. En este caso, como se ve en los requisitos, s贸lo necesitamos el par谩metro apikeyComo tenemos que pasarle nuestra variable key, lo hacemos usando template strings. 

5. Utilizamos la fetch API para combinar las variables base + query, creando as铆 un verdadero API endpoint. Utilizamos await para esperar mientras se resuelve la promesa, y guardamos todo en una variable llamada response (mismo proceso que para la primera request)

6. Pasamos la response a formato JSON y la guardamos en una variable llamada data. Hacemos un console.log de data para ver si todo ha salido como esperamos.

const getWeather = async (id) => {

    const base = `http://dataservice.accuweather.com/currentconditions/v1/${id}`;
    const query = `?apikey=${key}`;

    const response = await fetch(base + query);
    const data = await response.json();

    console.log(data);
};

7. Llamamos a la funci贸n getWeather. Para que la funci贸n getCity no se interponga en nuestro camino, la comentamos temporalmente. La funci贸n getWeather espera un city code, as铆 que de momento le pasamos el que nos ha devuelto la primera request. 

// getCity('Zurich')
//     .then(data => console.log(data))
//     .catch(err => console.log(err));

getWeather("316622");

隆Y ah铆 lo tenemos! 鉁 Ahora en nuestra consola podemos ver un array con 煤nicamente un elemento (un objeto que contiene la informaci贸n meteorol贸gica de Zurich). Pero tenemos que pulir este sistema, porque en la pr谩ctica nadie busca una ciudad por su c贸digo 煤nico, sino por su nombre (Zurich, Barcelona, etc). 馃槵

8. As铆 que borramos la llamada a la funci贸n getWeather, descomentamos la funci贸n getCity y hacemos un return de data dentro de la funci贸n getWeather en lugar de un console.log. En lugar de hacer un return de todo el array, lo hacemos del primer (y 煤nico) elemento, porque as铆 nos ser谩 m谩s f谩cil trabajar con 茅l.

    return data[0];

9. Cuando invocamos a la funci贸n getCity, localizamos el punto donde obtenemos la informaci贸n de dicha ciudad.

getCity('Zurich')
    .then(data => console.log(data))
    .catch(err => console.log(err));

Ah铆 es donde debemos invocar a la funci贸n getWeather. 馃鈥嶁檪锔

10. En lugar de hacer un console.log de data, obtenemos la propiedad Key contenida en ese par谩metro y se la pasamos a la funci贸n getWeather. Recuerda que debemos hacer un return de la funci贸n.

getCity('Zurich')
    .then(data => {
        return getWeather(data.Key);
    })
    .catch(err => console.log(err));

11. Ya que eso devuelve otra promesa, podemos vincularle el m茅todo then() para gestionar qu茅 sucede cuando la promesa se resuelve. Por ahora hacemos s贸lo un console.log de los datos provenientes de la funci贸n getWeather. 

getCity('Zurich')
    .then(data => {
        return getWeather(data.Key);
    }).then(data => {
        console.log(data);
    })
    .catch(err => console.log(err));

隆Y ya lo tenemos! 馃憣 En nuestra consola podemos ver ahora las condiciones meteorol贸gicas de Zurich. 

Es momento de trabajar en la funcionalidad de nuestra app, para que un usuario pueda escribir el nombre de una ciudad y recibir la predicci贸n meteorol贸gica correspondiente. Pero eso ser谩 en el pr贸ximo cap铆tulo de esta serie. 馃槈

THE END!

隆Y con esto terminamos la parte #1 de este proyecto! Espero que hayas aprendido algo nuevo 馃槉.  Si te queda alguna duda, 隆nos vemos en los comentarios!

Si quieres seguir aprendiendo, puedes ver la parte #2 aqu铆.

Y si crees que este post puede serle 煤til a alguien, 隆comp谩rtelo!

Otros art铆culos que pueden interesarte

D铆as del 353 al 386
Objetivos versus realidad Y nuevamente, lleg贸 otro d铆a clave. Lleg贸鈥 pas贸. El pasado 4 de marzo este Reto Computer Geek[...]
Angular: Entendiendo la Directiva ngModel
Angular es un framework que nos permite, entre otras cosas, a帽adir contenido din谩mico a nuestros archivos HTML. Una de las formas[...]
D铆as del 2 al 4
"Si buscas resultados distintos no hagas siempre lo mismo" - Albert Einstein Estos d铆as estoy aprendiendo a hacer loops en[...]

Deja un comentario

Este sitio usa Akismet para reducir el spam. Aprende c贸mo se procesan los datos de tus comentarios.

Como toda web legal que se precie, utilizamos cookies para asegurar que damos la mejor experiencia al usuario en nuestro sitio web. Si contin煤as utilizando este sitio asumiremos que est谩s de acuerdo. m谩s informaci贸n

Los ajustes de cookies de esta web est谩n configurados para "permitir cookies" y as铆 ofrecerte la mejor experiencia de navegaci贸n posible. Si sigues utilizando esta web sin cambiar tus ajustes de cookies o haces clic en "Aceptar" estar谩s dando tu consentimiento a esto.

Cerrar